微服务笔记 注册中心 基础知识:
访问注册中心到服务时将以前到地址变成服务名 Eureka现在已停止更新 Eureka 支持AP,Zookeeper与Consul支持CP 在启动类可以开启**@EnableDiscoveryClient**服务发现 在RestTemplate配置类上要加@LoadBalanced来实现负载均衡(默认轮询) Eureka 一、客户端 :主启动类上配置**@EnableEurekaClient**开启服务
依赖 1 2 3 4 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 server: port: 80 spring: application: name: cloud-order-service eureka: client: register-with-eureka: true fetchRegistry: true service-url: defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/
二、服务端 : 主启动类上配置**@EnableEurekaServer**开启服务
依赖 1 2 3 4 5 <!--eureka-server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 server: port: 8001 spring: application: name: cloud-payment-service eureka: client: register-with-eureka: true fetchRegistry: true service-url: defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/ instance: instance-id: payment8001 prefer-ip-address: true lease-renewal-interval-in-seconds: 1 lease-expiration-duration-in-seconds: 2
Zookeeper zookeeper需要安装到linux当中,没有ui界面,java语言开发
依赖 客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> <!--先排除自带的zookeeper--> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency> <!--添加zookeeper3.4.9版本--> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.9</version> </dependency>
服务端
1 2 3 4 5 <!-- SpringBoot整合zookeeper客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> </dependency>
配置文件 1 2 3 4 5 6 7 8 9 10 server: port: 8004 spring: application: name: cloud-provider-payment cloud: zookeeper: connect-string: 127.0 .0 .1 :2181
Consul Consulr需要安装到linux当中,有ui界面,go语言开发
依赖 1 2 3 4 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 server: port: 80 spring: application: name: cloud-consumer-order cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name}
负载均衡(LB) 负载均衡优点:高可用
Ribbon 客户端的负载均衡工具,提供负载均衡算法和服务调用
Ribbon本地负载均衡与Nginx服务端负载均衡区别
Nginx是集中式的,客户端的所有请求都会请求nginx,由nginx转发,负载均衡由服务端实现,可以理解为挡在最前面的
Ribbon是进程内的,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
依赖 注意:新版的Eureka自带了Ribbon可以不用引入 1 2 3 4 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
一、RestTemplate的使用 getForObject方法/getForEntity方法
postForObject/postForEntity
Object 返回对象为响应体中数据转化成的对象,基本上可以理解为Json
Entity 返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
二、Ribbon默认自带的负载规则 IRule:根据特定算法中从服务列表中选取一个要访问的服务
轮询 随机 重试:先轮询失败就重试 重试扩展:先轮询失败就重试,响应速度越快权重越大,越容易被选择
三、负载规则替换 注意:这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下
所以在启动类包之外可以再建一个规则包,放配置类
1 2 3 4 5 6 7 @Configuration public class MyselfRule { @Bean public IRule myRule () { return new RandomRule (); } }
主启动类添加**@RibbonClient**(name = “CLOUD-PAYMENT-SERVICE”,configuration = MyselfRule.class)
参数说明:
name 要去访问那个服务 configuration配置的负载规则 四、Ribbon轮询算法原理 负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。
List instances = discoveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”);
如: List [0] instances = 127.0.0.1:8002
List [1] instances = 127.0.0.1:8001
8001+ 8002 组合成为集群,它们共计2台机器,集群总数为2, 按照轮询算法原理:
当总请求数为1时: 1 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位2时: 2 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
当总请求数位3时: 3 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位4时: 4 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
如此类推……
五、源码分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 package com.netflix.loadbalancer;import com.netflix.client.config.IClientConfig;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class RoundRobinRule extends AbstractLoadBalancerRule { private AtomicInteger nextServerCyclicCounter; private static final boolean AVAILABLE_ONLY_SERVERS = true ; private static final boolean ALL_SERVERS = false ; private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class); public RoundRobinRule () { this .nextServerCyclicCounter = new AtomicInteger (0 ); } public RoundRobinRule (ILoadBalancer lb) { this (); this .setLoadBalancer(lb); } public Server choose (ILoadBalancer lb, Object key) { if (lb == null ) { log.warn("no load balancer" ); return null ; } else { Server server = null ; int count = 0 ; while (true ) { if (server == null && count++ < 10 ) { List<Server> reachableServers = lb.getReachableServers(); List<Server> allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if (upCount != 0 && serverCount != 0 ) { int nextServerIndex = this .incrementAndGetModulo(serverCount); server = (Server)allServers.get(nextServerIndex); if (server == null ) { Thread.yield (); } else { if (server.isAlive() && server.isReadyToServe()) { return server; } server = null ; } continue ; } log.warn("No up servers available from load balancer: " + lb); return null ; } if (count >= 10 ) { log.warn("No available alive servers after 10 tries from load balancer: " + lb); } return server; } } } private int incrementAndGetModulo (int modulo) { int current; int next; do { current = this .nextServerCyclicCounter.get(); next = (current + 1 ) % modulo; } while (!this .nextServerCyclicCounter.compareAndSet(current, next)); return next; } public Server choose (Object key) { return this .choose(this .getLoadBalancer(), key); } public void initWithNiwsConfig (IClientConfig clientConfig) { } }
六、手写轮训 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package cn.org.alan.cloud.lb;import org.springframework.cloud.client.ServiceInstance;import java.util.List;public interface LoadBalancer { ServiceInstance instances (List<ServiceInstance> serviceInstances) ; }package cn.org.alan.cloud.lb;import org.springframework.cloud.client.ServiceInstance;import org.springframework.stereotype.Component;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;@Component public class MyLB implements LoadBalancer { private AtomicInteger atomicInteger = new AtomicInteger (0 ); public final int getAndIncrement () { int current; int next; do { current = this .atomicInteger.get(); next =current >= 2147483647 ? 0 : current + 1 ; }while (!this .atomicInteger.compareAndSet(current,next)); System.out.println("next:" +next); return next; } @Override public ServiceInstance instances (List<ServiceInstance> serviceInstances) { int index = getAndIncrement() % serviceInstances.size(); return serviceInstances.get(index); } } @GetMapping("/consumer/payment/lb") public String getPaymentLB () { List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE" ); if (instances==null ||instances.size()<=0 ){ return null ; } ServiceInstance serviceInstance = loadBalancer.instances(instances); URI uri = serviceInstance.getUri(); return restTemplate.getForObject(uri+"/payment/lb" ,String.class); }
服务调用 OpenFeign 一、使用 依赖 1 2 3 4 5 <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
开启OpenFeign 在主启动类上添加**@EnableFeignClients**注解
接口+注解:微服务调用接口+@FeignClient(“要访问的服务名称”)
举例:
Service
1 2 3 4 5 6 7 @Component @FeignClient("CLOUD-PAYMENT-SERVICE") public interface PaymentFeignService { @GetMapping(value = "/payment/get/{id}") public CommonResult getPaymentById (@PathVariable("id") Long id) ; }
controller
1 2 3 4 5 6 7 8 9 10 @RestController public class OrderFeignController { @Resource private PaymentFeignService paymentFeignService; @GetMapping(value = "/consumer/payment/get/{id}") public CommonResult getPaymentById (@PathVariable("id") Long id) { return paymentFeignService.getPaymentById(id); } }
二、超时控制 OpenFeign默认等待1秒钟,超过后报错
配置超时
1 2 3 4 5 6 ribbon: ReadTimeout: 5000 ConnectTimeout: 5000
三、日志打印功能 Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
日志级别 1 2 3 4 NONE:默认的,不显示任何日志; BASIC:仅记录请求方法、URL、响应状态码及执行时间; HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息; FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。
配置步骤
配置文件开启日志 1 2 3 4 logging: level: cn.org.alan.cloud.service.PaymentFeignService: debug
配置Bean 1 2 3 4 5 6 7 @Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel () { return Logger.Level.FULL; } }
服务降级 Hystrix 一、重要概念
服务降级(fallback)
服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback
会发生降级的情况
a. 程序运行异常
b. 超时
c. 服务熔断触发服务降级
d. 线程池/信号量打满也会导致服务降级
服务熔断(break)
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
服务的降级->进而熔断->恢复调用链路
服务限流(flowlimit)
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
二、使用
依赖 1 2 3 4 5 <!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
服务端,降级配置 @EnableCircuitBreaker 与**@HystrixCommand**
2.1 先在主启动类上加 @EnableCircuitBreaker 注解激活hystrix
2.2 然后在方法上加@HystrixCommand设置降级方法与规则
1 2 3 4 5 @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000") })
配置消费端**@EnableHystrix**和 @HystrixCommand
3.1 添加配置文件
1 2 3 feign: hystrix: enabled: true
3.2 启动类添加注解**@EnableHystrix**
3.3 服务添加**@HystrixCommand**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @RestController @Slf4j public class OrderHystirxController { @Resource private PaymentHystrixService paymentHystrixService; @GetMapping("/consumer/payment/hystrix/ok/{id}") public String paymentInfo_OK (@PathVariable("id") Integer id) { String result = paymentHystrixService.paymentInfo_OK(id); return result; } @GetMapping("/consumer/payment/hystrix/timeout/{id}") @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500") }) public String paymentInfo_TimeOut (@PathVariable("id") Integer id) { String result = paymentHystrixService.paymentInfo_TimeOut(id); return result; } public String paymentTimeOutFallbackMethod (@PathVariable("id") Integer id) { return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o" ; } }
1 2 3 4 5 @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000") })
引入新标签**@DefaultProperties(defaultFallback = “”)**:添加默认defaultFallback,defaultFallback是上面HystrixCommand中fallbackMethod方法
当我引入DefaultProperties后,我们在方法上就可以标注@HystrixCommand不用写后面的一堆内容,这是通用方法,如果需要单独配置的就使用上面的@HystrixCommand(fallbackMethod=””)
降级处理在客户端80实现完成
根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理
1 @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
总结:就是将之前的service接口去写一个类去实现,然后在openfeign注解中设置这个类为fallback,当方法出问题时就会去调用各自的实现方法
服务熔断
参数可以在HystrixCommandProperties接口中找到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = { @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 是否开启断路器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 请求次数 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 时间窗口期 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),// 失败率达到多少后跳闸 }) public String paymentCircuitBreaker (@PathVariable("id") Integer id) { if (id < 0 ) { throw new RuntimeException ("******id 不能负数" ); } String serialNumber = IdUtil.simpleUUID(); return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber; } public String paymentCircuitBreaker_fallback (@PathVariable("id") Integer id) { return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " + id; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 @HystrixCommand(fallbackMethod = "str_fallbackMethod", groupKey = "strGroupCommand", commandKey = "strCommand", threadPoolKey = "strThreadPool", commandProperties = { // 设置隔离策略,THREAD 表示线程池 SEMAPHORE:信号池隔离 @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"), // 当隔离策略选择信号池隔离的时候,用来设置信号池的大小(最大并发数) @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"), // 配置命令执行的超时时间 @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"), // 是否启用超时时间 @HystrixProperty(name = "execution.timeout.enabled", value = "true"), // 执行超时的时候是否中断 @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"), // 执行被取消的时候是否中断 @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"), // 允许回调方法执行的最大并发数 @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"), // 服务降级是否启用,是否执行回调函数 @HystrixProperty(name = "fallback.enabled", value = "true"), // 是否启用断路器 @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为 20 的时候, // 如果滚动时间窗(默认10秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"), // 该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在请求数量超过 // circuitBreaker.requestVolumeThreshold 的情况下,如果错误请求数的百分比超过50, // 就把断路器设置为 "打开" 状态,否则就设置为 "关闭" 状态。 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"), // 该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后, // 会将断路器置为 "半开" 状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为 "打开" 状态, // 如果成功就设置为 "关闭" 状态。 @HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"), // 断路器强制打开 @HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"), // 断路器强制关闭 @HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"), // 滚动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间 @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"), // 该属性用来设置滚动时间窗统计指标信息时划分"桶"的数量,断路器在收集指标信息的时候会根据 // 设置的时间窗长度拆分成多个 "桶" 来累计各度量值,每个"桶"记录了一段时间内的采集指标。 // 比如 10 秒内拆分成 10 个"桶"收集这样,所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常 @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"), // 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。 @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"), // 该属性用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。 @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"), // 该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。 @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"), // 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数, // 就从最初的位置开始重写。例如,将该值设置为100, 滚动窗口为10秒,若在10秒内一个 “桶 ”中发生了500次执行, // 那么该 “桶” 中只保留 最后的100次执行的统计。另外,增加该值的大小将会增加内存量的消耗,并增加排序百分位数所需的计算时间。 @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"), // 该属性用来设置采集影响断路器状态的健康快照(请求的成功、 错误百分比)的间隔等待时间。 @HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"), // 是否开启请求缓存 @HystrixProperty(name = "requestCache.enabled", value = "true"), // HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中 @HystrixProperty(name = "requestLog.enabled", value = "true"), }, threadPoolProperties = { // 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量 @HystrixProperty(name = "coreSize", value = "10"), // 该参数用来设置线程池的最大队列大小。当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列, // 否则将使用 LinkedBlockingQueue 实现的队列。 @HystrixProperty(name = "maxQueueSize", value = "-1"), // 该参数用来为队列设置拒绝阈值。 通过该参数, 即使队列没有达到最大值也能拒绝请求。 // 该参数主要是对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue // 队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了。 @HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"), } ) public String strConsumer () { return "hello 2020" ; }public String str_fallbackMethod () { return "*****fall back str_fallbackMethod" ; }
服务限流,后面alibaba的sentinel记录
依赖 1 2 3 4 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>
启动类添加**@EnableHystrixDashboard**激活hystrixDashboard 服务启动类中添加, 不过要确保添加@EnableCircuitBreaker开启了Hystrix
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Bean public ServletRegistrationBean getServlet () { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet (); ServletRegistrationBean registrationBean = new ServletRegistrationBean (streamServlet); registrationBean.setLoadOnStartup(1 ); registrationBean.addUrlMappings("/hystrix.stream" ); registrationBean.setName("HystrixMetricsStreamServlet" ); return registrationBean; }
网关 Gateway网关 三大核心概念:
Route(路由) Predicate(断言) Filter(过滤) Gateway工作流程:
客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。
Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
Filter在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,
在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
使用 依赖 1 2 3 4 5 <!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
小细节:
引入了gateway就不用引入web和actuator依赖,不然会报错
配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 spring: application: name: cloud-gateway cloud: gateway: routes: - id: payment_routh uri: http://localhost:8001 predicates: - Path=/payment/get/** - id: payment_routh2 uri: http://localhost:8001 predicates: - Path=/payment/lb/**
配置文件的两种配置方式
直接在配置文件yml中配置如上, 代码中注入RouteLocator的Bean 1 2 3 4 5 6 7 8 9 10 11 @Configuration public class GateWayConfig { @Bean public RouteLocator customrouteLocator (RouteLocatorBuilder routeLocatorBuilder) { RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); routes.route("path_route" , r->r.path("/wiki" ) .uri("https://alan.org.cn" )).build(); return routes.build(); } }
上面存在的问题:
地址写死 微服务中不可能只有一台机器,还需要负载均衡 配置动态路由 动态路由其实就是把写死的地址换成我们注册中心的服务名
更改后的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true routes: - id: payment_routh uri: lb://CLOUD-PAYMENT-SERVICE predicates: - Path=/payment/get/** - id: payment_routh2 uri: lb://CLOUD-PAYMENT-SERVICE predicates: - Path=/payment/lb/** eureka: instance: hostname: cloud-gateway-service client: service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
常用的Route Predicate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 predicates: - Path=/payment/lb/** - After=2020-02-05T15:10:03.685+08:00[Asia/Shanghai] - Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai] - Between=2020-02-02T17:45:06.206+08:00[Asia/Shanghai],2020-03-25T18:59:06.206+08:00[Asia/Shanghai] - Cookie=username,zzyy - Header=X-Request-Id, \d+ - Host=**.alan.org.cn - Method=GET - Query=username, \d+
Filter 常用的GatewayFilter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true routes: - id: payment_routh uri: lb://CLOUD-PAYMENT-SERVICE filters: - AddRequestParameter=X-Request-Id,1024 predicates: - Path=/payment/get/** - id: payment_routh2 uri: lb://CLOUD-PAYMENT-SERVICE predicates: - Path=/payment/lb/** eureka: instance: hostname: cloud-gateway-service client: service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
自定义过滤器 主要两个接口implements GlobalFilter ,Ordered
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Component @Slf4j public class MyLogGateWayFilter implements GlobalFilter , Ordered { @Override public Mono<Void> filter (ServerWebExchange exchange, GatewayFilterChain chain) { log.info("Come in MyLogGateWayFilter" +new Date ()); String uname = exchange.getRequest().getQueryParams().getFirst("uname" ); if (uname==null ){ log.info("用户名为null,非法用户" ); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder () { return 0 ; } }