美文网首页Spring Cloud
springCloud --- 中级篇(1)

springCloud --- 中级篇(1)

作者: 贪挽懒月 | 来源:发表于2020-06-07 17:19 被阅读0次

    本系列笔记涉及到的代码在GitHub上,地址:https://github.com/zsllsz/cloud

    本文涉及知识点:

    • 服务降级熔断之hystrix;

    • 服务网关之gateway;


    欢迎大家关注我的公众号 javawebkf,目前正在慢慢地将简书文章搬到公众号,以后简书和公众号文章将同步更新,且简书上的付费文章在公众号上将免费。


    一、服务降级&熔断&限流之Hystrix(豪猪)

    1、微服务面临的问题:
    将一个个的业务拆分出来,独立成一个个服务,降低了系统的耦合度,但是也面临了一些问题。比如实现业务场景一需要调用服务A,A又要调用B,B还要调用C……,这叫做“扇出”,就是各个微服务相互调用,像一把折扇一样。如果B挂了,那就导致整条链路不能用了,就出现了服务雪崩的情况。为了解决这种问题,就需要一种名为“服务降级和熔断”的办法。

    2、Hystrix是什么?
    Hystrix就是服务降级和熔断的一种落地实现。可以保证当某个服务超时、异常的情况下,不会导致整体服务雪崩,避免级联故障,提高分布式系统的弹性。它会在当某个服务超时、异常的时候,返回一个符合预期格式、可处理的备选响应给调用者,从而保证了服务的可用。

    3、Hystrix能干嘛:

    • 服务降级:fallback,比如我们写代码的时候,有下面这种情况:
    if () {
    
    } else if () {
    
    } else {
    
    }
    

    这里最后的else就是服务降级,就是给一个兜底的解决方案。 会发生服务降级的情况:程序异常、响应超时、服务熔断触发降级、线程池/信号量打满也会导致服务降级。

    • 服务熔断:break,就像保险丝熔断一样,当服务达到了所能承受的最大访问量后,就拉闸断电,调用服务降级给用户返回友好的提示。

    • 服务限流:flowlimit,在秒杀等高并发的场景下,严禁一窝蜂地访问系统,而是排队访问,一秒钟N个,有序进行。

    4、cloud-provider-hystrix-payment8001项目搭建:
    新建一个名为cloud-provider-hystrix-payment8001的module。

    • pom.xml:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator </artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!-- hystrix -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    
    • application.yml:
    server:
      port: 8001
      
    spring:
      application:
        name: cloud-provider-hystrix-payment
        
    eureka:
      client:
        register-with-eureka: true 
        fetch-registry: true 
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka #eureka server
    
    • 启动类:
    @SpringBootApplication
    @EnableEurekaClient
    public class PaymentHystrixMain8001 {
        public static void main(String[] args) throws Exception {
            SpringApplication.run(PaymentHystrixMain8001.class, args);
        }
    }
    
    • service:
    @Service
    public class PaymentService {
    
        public String paymentOk(Integer id) {
            return " thats ok";
        }
        
        public String paymentError(Integer id) {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "somethind wrong";
        }
    }
    

    这里一个方法模拟正常情况,一个方法模拟超时的情况。然后在controller中调这两个方法。

    • controller:
    @RestController
    @RequestMapping("/payment")
    public class PaymentController {
    
        @Autowired
        private PaymentService paymentService;
        @Value("${server.port}")
        private String port;
        
        @GetMapping("/ok/{id}")
        public String paymentOk(@PathVariable("id") Integer id) {
            return paymentService.paymentOk(id) + "\r\n" + port;
        }
        
        @GetMapping("/error/{id}")
        public String paymentError(@PathVariable("id") Integer id) {
            return paymentService.paymentError(id) + "\r\n" + port;
        }
    }
    

    启动7001的eureka server和这个8001,访问从controller中的两个接口,测试一下。在非高并发的情况下,两个接口都可以正常返回数据,只不过paymentOk可以立即响应,而paymentError就要等待5秒才会响应。但是,如果搞个jmeter对paymentError接口进行压测,20000个线程并发去请求paymentError,然后你再用浏览器访问paymentOk,发现paymentOk也被拖慢了。如果线程再多一点儿,可能就会访问不了了。可明明是paymentError接口响应慢,paymentOk是不应该受影响的。所以可以用hystrix对服务进行降级。下面先新建一个consumer调用这个payment服务,然后再在这个consumer中对服务进行降级。

    5、cloud-consumer-feign-hystrix-order80项目搭建:
    新建名为cloud-consumer-feign-hystrix-order80的module。

    • pom.xml:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator </artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!-- hystrix -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <!-- openfeign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
    • yml:
    server:
      port: 80
      
    eureka:
      client:
        register-with-eureka: false
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka
    
    • 主启动类:
    @SpringBootApplication
    @EnableFeignClients
    public class OrderHystrixMain80 {
        public static void main(String[] args) throws Exception {
            SpringApplication.run(OrderHystrixMain80.class, args);
        }
    }
    
    • service:
    @Service
    @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
    public interface PaymentHystrixService {
        
        @GetMapping("payment/ok/{id}")
        public String paymentOk(@PathVariable("id") Integer id);
        
        @GetMapping("payment/error/{id}")
        public String paymentError(@PathVariable("id") Integer id);
    }
    
    • controller:
    @RestController
    @RequestMapping("/order")
    public class OrderHystrixController {
    
        @Autowired
        private PaymentHystrixService phService;
        
        @GetMapping("/hystrix/ok/{id}")
        public String paymentOk(@PathVariable("id") Integer id) {
            return phService.paymentOk(id);
        }
        
        @GetMapping("/hystrix/error/{id}")
        public String paymentError(@PathVariable("id") Integer id) {
            return phService.paymentError(id);
        }
    }
    

    启动后,通过浏览器访问http://localhost/order/hystrix/ok/1,发现是立即响应的,访问http://localhost/order/hystrix/error/1发现报500了,控制台报错 Read timed out。这是因为openfeign默认超时时间是1秒,而error接口又设置了线程睡5秒。可以在order80的配置文件中配置openfeign超时时间,设置大于5秒,就可以正常访问。

    ribbon:
      # 建立连接后从服务器获取可用资源的时间
      ReadTimeout: 6000
      # 建立连接所有的时间
      ConnectTimeout: 6000
    

    这是没有并发的情况,再使用jmeter进行压测,那么这两个接口都得凉凉了。

    6、如何解决上面的问题?
    服务提供方payment8001:

    • 如果8001超时,不能让order80一直转圈圈等待,要进行payment8001服务降级
    • 如果8001挂掉了,也不能让order80一直等待,要进行payment8001服务降级

    服务调用方order80:

    • 如果payment8001没问题,order80自己出故障了,那么order80要进行服务降级

    7、payment8001服务降级配置:
    payment8001的paymentError方法,线程睡了5秒,所以至少5秒才会有响应。假设我们认为这个方法正常是3秒就能响应完的,超过3秒就要进行服务降级。那么我们就设置超时时间峰值为3秒,超过了3秒就要有兜底的方法。

    • 首先在启动类上加上@EnableCircuitBreaker注解激活hystrix功能;
    • 然后在paymentError方法上加上@HystrixCommand注解,并且配置超时调用的方法,如下:
    @HystrixCommand(fallbackMethod = "paymentError_default", commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
        })
        public String paymentError(Integer id) {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "somethind wrong";
        }
        
        public String paymentError_default(Integer id) {
            return "这是兜底的方法";
        }
    

    这段代码的意思就是,paymentError方法至少要5秒才会响应,但是我加上了hystrix的注解,设置了最大响应时长为3秒,超过3秒,那就走兜底的方法。所以现在启动payment8001访问paymentError方法,应该会打出"这是兜底的方法"这句话。

    hystrix服务降级
    说明hystrix配置成功了,并且除了超时,如果paymentError报异常了,比如写一个int x = 10 / 0,也会走兜底的方法。

    8、消费端order80的服务降级:
    消费端服务降级也一样的配置,在主启动类上加@EnableCircuitBreaker注解,然后controller写成下面这样:

    @GetMapping("/hystrix/error/{id}")
    @HystrixCommand(fallbackMethod = "paymentError_default", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentError(@PathVariable("id") Integer id) {
        return phService.paymentError(id);
    }
        
    public String paymentError_default(@PathVariable("id")Integer id) {
        return "这是消费端兜底的方法";
    }
    

    配置超过3秒就走兜底的方法。而服务端payment8001对应的方法设置了线程睡5秒,超过了3秒才会有响应,所以这里会走兜底的方法。注意ribbon默认超时时间是1秒,所以如果修改ribbon超时时间的话,即使payment8001中设置线程睡2秒,order80中hystrix配置超时时间3秒,也会走兜底的方法,因为是否超时优先取的是ribbon配置的。

    9、全局降级配置:
    上面针对需要服务降级的两个方法加上了hystrix相关配置,如果还有大量的方法也需要服务降级,在每个方法上都加上这么一段注解,不太方便,所以就出现了全局降级配置。比如要在order80上做全局服务降级配置,只需要将controller改成下面这样即可:

    @RestController
    @RequestMapping("/order")
    @DefaultProperties(defaultFallback = "defaultMethod", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public class OrderHystrixController {
    
        @Autowired
        private PaymentHystrixService phService;
        
        @GetMapping("/hystrix/ok/{id}")
        @HystrixCommand
        public String paymentOk(@PathVariable("id") Integer id) {
            return phService.paymentOk(id);
        }
        
        @GetMapping("/hystrix/error/{id}")
        @HystrixCommand(fallbackMethod = "paymentError_default", commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
        })
        public String paymentError(@PathVariable("id") Integer id) {
            return phService.paymentError(id);
        }
        
        public String paymentError_default(@PathVariable("id")Integer id) {
            return "这是消费端兜底的方法";
        }
        
        public String defaultMethod() {
            return "我是全局默认的服务降级配置";
        }
    }
    

    首先在类上加注解,并配置超时时间,然后需要用全局降级的方法上加@HystrixCommand注解。这里用paymentOk方法来演示,payment8001端的paymentOk方法也设置线程睡4秒钟,paymentOk方法应该会打印出“我是全局默认的服务降级配置”这句话。

    全局降级配置

    然后将payment8001的paymentOk方法改成睡2秒钟,那么就会正常返回:


    正常访问

    然后paymentError方法,因为在方法上配置了hystrix相关配置,所以还是走方法对应的配置,而不是走全局。

    但是以上做法还是不够完美,因为order80调用payment8001,所有接口都是通过openfeign去调用的,擒贼先擒王,所以最好的做法就是在openfeign调用payment8001的接口中添加实现去做降级。具体做法如下:

    • 修改yml:
    feign:
      hystrix:
        enabled: true
    
    • order80的service改成这样:
    @Service
    @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentHystrixServiceImpl.class)
    public interface PaymentHystrixService {
        
        @GetMapping("payment/ok/{id}")
        public String paymentOk(@PathVariable("id") Integer id);
        
        @GetMapping("payment/error/{id}")
        public String paymentError(@PathVariable("id") Integer id);
    }
    
    • 新建service的实现:
    @Component
    public class PaymentHystrixServiceImpl implements PaymentHystrixService{
    
        @Override
        public String paymentOk(Integer id) {
            return "paymentOk-default-return";
        }
    
        @Override
        public String paymentError(Integer id) {
            return "paymentError-default-return";
        }
    
    }
    
    • 最后把controller中hystrix相关注解都去掉。

    这样就搞定了,当访问8001发生异常、或者8001压根儿就没启动时,就会执行serviceImpl里面的方法。

    10、服务熔断:
    当微服务调用链路的某个服务不可用了或者响应时间太长了,会对服务进行降级,快速返回友好的响应信息,当检测到该节点正常了之后,又会恢复调用。即一开始是close状态,检测到异常就变成open状态,然后再变成half open状态,恢复一部分调用,如果没问题再变成close。下面就演示服务熔断。

    • 首先在payment8001的service中加如下代码:
    @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"), // 失败率达到多少后跳闸
                // 10次请求有6次是失败的,就进行服务熔断,10秒后会变为half open状态,看看能否请求成功,如果成功就变成close状态
        })
        public String paymentCircuitBreaker(Integer id) {
            if (id < 0) {
                throw new RuntimeException("id不能为负数");
            }
            return "调用成功";
        }
        
        public String paymentCircuitBreaker_fallback(Integer id) {
            return "id不能为负数";
        }
    

    这几个注解的意思就是在10次访问中,如果失败了6次,那就熔断,返回“id不能为负数这句话”,这个时候,即使你传的id是正数,也会返回这句话,因为这个时候服务熔断了。10000 milliseconds后,会变成半开状态,试着放行一个请求,如果此时你请求的id是正数,那么就返回“调用成功”,并且解除熔断。

    • 在controller中新增如下方法:
    @GetMapping("/breaker/{id}")
        public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
            return paymentService.paymentCircuitBreaker(id);
        }
    

    可以访问该接口,先传入负数id,然后满足10次有6次失败的情况下,再将id变为正数传入,发现也是返回“id不能为负数”这句话。过一段时间后才恢复正常,说明服务熔断起作用了。

    11、hystrix的图形化界面:

    • 新建一个cloud-consumer-hystrix-dashboard9001的module
    • pom.xml:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator </artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix-dashboard -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>
    
    • 主启动类:
    @SpringBootApplication
    @EnableHystrixDashboard
    public class HystrixDashBoardMain9001 {
    
        public static void main(String[] args) throws Exception {
            SpringApplication.run(HystrixDashBoardMain9001.class, args);
        }
    }
    
    • 如果要监控8001,那么需要在8001中配置如下bean:
    @Configuration
    public class DashBoardConfig {
    
        @Bean
        public ServletRegistrationBean<HystrixMetricsStreamServlet> getBean() {
            HystrixMetricsStreamServlet bean = new HystrixMetricsStreamServlet();
            ServletRegistrationBean<HystrixMetricsStreamServlet> register = new ServletRegistrationBean<HystrixMetricsStreamServlet>(bean);
            register.setLoadOnStartup(1);
            register.addUrlMappings("/hystrix.stream");
            register.setName("HystrixMetricsStreamServlet");
            return register;
        }
    }
    
    • 然后访问 localhost:9001/hystrix,在页面中填写如下信息:


      dashboard

      填好后回车,然后再去访问8001,那么8001的访问信息就会出现在dashboard中了。


      dashboard

    二、服务网关之spring gateway

    1、服务网关是来干嘛的?
    一般来说,用户访问首先是到nginx,nginx会做负载均衡,然后并不是直接访问各位微服务应用,而是先到gateway。gateway可以做路由转发、权限校验、流量控制等。功能类似的框架还有zuul和zuul2,zuul停更了,zuul2还没完善出来,所以spring自己搞了一套gateway。

    2、gateway核心概念:

    • 路由Route:简单的理解为url,如果断言为true就匹配该url
    • 断言Predicate:路由转发的判断条件
    • 过滤Filter:过滤器

    3、gateway工作流程:
    核心逻辑是路由转发 + 执行过滤链。客户端向gateway发请求,然后在gateway handler mapping 找到相匹配的路由,将请求发送到gateway web handler,handler再通过过滤链将请求发送到微服务中,然后返回。

    4、代码演示:

    • 新建名为cloud-gateway-gateway9527的module;
    • pom.xml:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <!-- eureka client -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!-- gateway -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    
    • yml:
    server:
      port: 9527
      
    spring:
      application:
        name: cloud-gateway
        
    eureka:
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
           defaultZone: http://eureka7001.com:7001/eureka
      instance:
        instance-id: cloud-gateway
        prefer-ip-address: true
    
    • 主启动:
    @SpringBootApplication
    @EnableEurekaClient
    public class GatewayMain9527 {
        public static void main(String[] args) throws Exception {
            SpringApplication.run(GatewayMain9527.class, args);
        }
    }
    

    8001的controller中有两个接口,现在我们可以直接通过接口路由访问到,但是我不想暴露8001端口,想通过9527端口去访问,做法如下:

    • 将9527的yml改成下面这样:
    server:
      port: 9527
      
    spring:
      application:
        name: cloud-gateway
      cloud:
        gateway:
          routes:
          - id: payment_route # id,要求唯一
            uri: http://localhost:8001 #提供服务的路由地址
            predicates:
            - Path=/payment/ok/** # 路径匹配的进行路由
            
          - id: payment_route # id,要求唯一
            uri: http://localhost:8001 #提供服务的路由地址
            predicates:
            - Path=/payment/error/** # 路径匹配的进行路由
              
        
    eureka:
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
           defaultZone: http://eureka7001.com:7001/eureka
      instance:
        instance-id: cloud-gateway
        prefer-ip-address: true
    

    现在依次启动7001的eureka、8001的payment和9527的gateway,然后访问呢localhost:9527/payment/ok/1,发现可以访问成功,说明路由转发成功。
    这是配置网关的第一种方式,还有硬编码的方式,如下:

    @Configuration
    public class GatewayConfig {
        @Bean
        public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
            RouteLocatorBuilder.Builder routes = builder.routes();
            // 意思就是访问localhost:9527/domestic将会转发到https://tuijian.hao123.com/domestic中去
            routes.route("path_id", r -> r.path("/domestic").uri("https://tuijian.hao123.com/domestic")).build();
            return routes.build();
        }
    }
    

    现在访问localhost:9527/domestic,结果如下:


    gateway路由转发

    5、动态路由:
    提供payment服务的有8001和8002,但是上面那样配置只写了8001,现在要实现通过微服务名称来做动态路由。即在配置那里不写死8001或8002,而是写8001和8002的微服务名称,这样就可以实现动态路由。

    • 首先确保启动了8001和8002;
    • 改yml:
    server:
      port: 9527
      
    spring:
      application:
        name: cloud-gateway
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true # 开启从注册中心动态创建路由的功能
          routes:
          - id: payment_route1 # id,要求唯一
            #uri: http://localhost:8001 #提供服务的路由地址
            uri: lb://CLOUD-PAYMENT-SERVICE #微服务名称
            predicates:
            - Path=/payment/** # 路径匹配的进行路由
            
          - id: payment_route2 # id,要求唯一
            #uri: http://localhost:8001 #提供服务的路由地址
            uri: lb://CLOUD-PAYMENT-SERVICE #微服务名称
            predicates:
            - Path=/payment/** # 路径匹配的进行路由
              
        
    eureka:
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
           defaultZone: http://eureka7001.com:7001/eureka
      instance:
        instance-id: cloud-gateway
        prefer-ip-address: true
    

    就这样就行了,然后访问http://localhost:9527/payment/1,就可以成功访问到8001和8002,并且会进行负载均衡,一次8001,一次8002,轮着来。

    6、常用断言:

     predicates:
     - Path=/payment/** # 路径匹配的进行路由
     - After=2020-05-04T20:47:11.281+08:00[Asia/Shanghai] # 配置的规则要在2020年5月4日20点20分后才生效,有after就有before和between
     - Cookie=username,test # 必须带cookie访问,cookie名叫username,值为test,值也可以写正则表达式
     - Header=X-Request-Id,\d+ # 必须带请求头访问,请求头名为X-Request-Id,值要满足 \d+ 这个正则表达式
     - Host=**.somehost.com,**.otherhost.com #请求域名必须匹配这两个,即请求时添加header,名为host,值匹配这两个即可
     - Method=GET #必须是get请求
     - Query=id,\d+ #必须有名为id的参数且值匹配 \d+ 这个正则
    

    7、常用filter:
    filter生命周期有两种,一种是在业务逻辑之前,一种是在业务逻辑之后;种类也是两种,一种gatewayFilter,一种globalFilter。用法和predicates一样,比如:

    filters:
    - AddRequestParameter=X-Request-Id,1024 # 请求必须带名为X-Request-Id的请求头,值为1024
    

    8、自定义过滤器:
    可以实现全局日志记录,统一网关鉴权等功能。

    • 新建自定义过滤器类:
    @Component
    @Slf4j
    public class MyFilter implements GlobalFilter, Ordered{
        /**
         * 表示这个过滤器的优先级,数字越小优先级越高
         */
        @Override
        public int getOrder() {
            return 0;
        }
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("=================进去自定义全局过滤器=============");
            // 获取id参数
            String id = exchange.getRequest().getQueryParams().getFirst("id");
            if (!"1".equals(id)) {
                log.error("================= id不存在==================");
                // 设置返回码
                exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange);
        }
    }
    

    也就是说,如果没有id这个参数或者id参数值不为1的,都会被拦截。实际生产中可以用这个来验证请求是否携带token。

    相关文章

      网友评论

        本文标题:springCloud --- 中级篇(1)

        本文链接:https://www.haomeiwen.com/subject/hwrkghtx.html