写点什么

Spring Cloud Gateway 自定义过滤器实战 (观测断路器状态变化)

用户头像
极客good
关注
发布于: 刚刚

import org.springframework.core.Ordered;


import org.springframework.http.HttpStatus;


import org.springframework.web.server.ServerWebExchange;


import reactor.core.publisher.Mono;


import java.lang.reflect.Method;


public class StatePrinterGatewayFilter implements GatewayFilter, Ordered {


private ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory;


// 通过构造方法取得 reactiveResilience4JCircuitBreakerFactory 实例


public StatePrinterGatewayFilter(ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory) {


this.reactiveResilience4JCircuitBreakerFactory = reactiveResilience4JCircuitBreakerFactory;


}


private CircuitBreaker circuitBreaker = null;


@Override


public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {


// 这里没有考虑并发的情况,如果是生产环境,请您自行添加上锁的逻辑


if (null==circuitBreaker) {


CircuitBreakerRegistry circuitBreakerRegistry = null;


try {


Method method = reactiveResilience4JCircuitBreakerFactory.getClass().getDeclaredMethod("getCircuitBreakerRegistry",(Class[]) null);


// 用反射将 getCircuitBreakerRegistry 方法设置为可访问


method.setAccessible(true);


// 用反射执行 getCircuitBreakerRegistry 方法,得到 circuitBreakerRegistry


circuitBreakerRegistry = (CircuitBreakerRegistry)method.invoke(reactiveResilience4JCircuitBreakerFactory);


} catch (Exception exception) {


exception.printStackTrace();


}


// 得到所有断路器实例


Seq<CircuitBreaker> seq = circuitBreakerRegistry.getAllCircuitBreakers();


// 用名字过滤,myCircuitBreaker 来自路由配置中


circuitBreaker = seq.filter(breaker -> breaker.getName().equals("myCircuitBreaker"))


.getOrNull();


}


// 取断路器状态,再判空一次,因为上面的操作未必能取到 circuitBreaker


String state = (null==circuitBreaker) ? "unknown" : circuitBreaker.getState().name();


System.out.println("state : " + state);


// 继续执行后面的逻辑


return chain.filter(exchange);


}


@Override


public int getOrder() {


return 10;


}


}


  • 接下来是 StatePrinterGatewayFilterFactory.java,这里用不上什么配置,所以 apply 方法的入参也就没用上,需要注意的是通过 Autowired 注解拿到了 reactiveResilience4JCircuitBreakerFactory,然后通过构造方法传递给了 StatePrinterGatewayFilter 实例:


package com.bolingcavalry.circuitbreakergateway.filter;


import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory;


import org.springframework.cloud.gateway.filter.GatewayFilter;


import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;


import org.springframework.stereotype.Component;


@Component


public class StatePrinterGatewayFilterFactory extends AbstractGatewayFilterFactory<Object>


{


@Autowired


ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory;


@Override


public String name() {


return "CircuitBreakerStatePrinter";


}


@Override


public GatewayFilter apply(Object config)


{


return new StatePrinterGatewayFilter(reactiveResilience4JCircuitBreakerFactory);


}


}


  • 最后是配置文件,完整的配置文件如下,可见我们将 CircuitBreakerStatePrinter 过滤器加了进来,放到最后:


server:


#服务端口


port: 8081


spring:


application:


name: circuitbreaker-gateway


cloud:


gateway:


routes:


  • id: path_route


uri: http://127.0.0.1:8082


predicates:


  • Path=/hello/**


filters:


  • name: CircuitBreaker


args:


name: myCircuitBreaker


  • name: CircuitBreakerStatePrinter


  • 再次运行单元测试类 CircuitbreakerTest.java,如下图红框所示,断路器状态已经打印出来,至此,我们可以精确把握断路器的状态变化了:


![在这里插入图片描述](https://img-blog.csdnimg.cn/1f894d376dc74e45ac2c50d48b870168.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA56iL5bqP5ZGY5qyj5a64,size_20,


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


color_FFFFFF,t_70,g_se,x_16)

分析请求被 filter 漏掉的问题

  • 有个很明显的问题,聪明睿智的您当然不会忽略:上图绿框中的连续四个响应,对应的断路器状态都没有打印出来,要知道,咱们的过滤器可是要处理每一个请求的,怎么会连续漏掉四个呢?

  • 其实原因很容易推理出来:断路器 CircuitBreaker 的 filter 先执行,然后才是咱们的 CircuitBreakerStatePrinter,而处于开启状态的断路器会直接返回错误给调用方,其后面的 filter 都不会执行了

  • 那么问题来了:如何控制 CircuitBreaker 和 CircuitBreakerStatePrinter 这两个 filter 的顺序,让 CircuitBreakerStatePrinter 先执行?

  • CircuitBreakerStatePrinter 是咱们自己写的代码,修改 StatePrinterGatewayFilter.getOrder 的返回值可以调整顺序,但 CircuitBreaker 不是咱自己的代码呀,这可如何是好?

  • 老规矩,看看断路器的源码,前文已经分析过了,断路器最重要的代码是 SpringCloudCircuitBreakerFilterFactory.apply 方法,如下图红框,生成的 filter 是 GatewayFilter 接口的实现类:



  • 再看加载过滤器到集合的那段关键代码,在 RouteDefinitionRouteLocator.loadGatewayFilters 方法中,如下图所示,由于 CircuitBreaker 的 filter 并没有实现 Ordered 接口,因此执行的是红框中的代码,代表其顺序的值等于 i+1,这个 i 就是遍历路由配置中所有过滤器时的一个从零开始的自增变量而已:



  • 回顾咱们的路由配置,CircuitBreaker 在前,CircuitBreakerStatePrinter 在后,所以,在添加 CircuitBreaker 的时候,i 等于 0,那么 CircuitBreaker 的 order 就等于 i+1=1 了

  • 而 CircuitBreakerStatePrinter 实现了 Ordered 接口,因此不会走红框中的代码,其 order 等于咱们写在代码中的值,咱们写的是 10

  • 所以:CircuitBreaker 的 order 等于 1,CircuitBreakerStatePrinter 等于 10,当然是 CircuitBreaker 先执行了!

再次修改

  • 知道了原因,改起来就容易了,我的做法很简单:StatePrinterGatewayFilter 不再实现 Ordered,这样就和 CircuitBreaker 的 filter 一样,执行的是上图红框中的代码,这样,在配置文件中,谁放在前面谁就先执行

  • 代码就不贴出来了,您自行删除 StatePrinterGatewayFilter 中和 Ordered 相关的部分即可

  • 配置文件调整后如下:


server:


#服务端口


port: 8081


spring:


application:


name: circuitbreaker-gateway


cloud:


gateway:


routes:


  • id: path_route


uri: http://127.0.0.1:8082


predicates:


  • Path=/hello/**


filters:


  • name: CircuitBreakerStatePrinter

  • name: CircuitBreaker


args:


name: myCircuitBreaker


  • 改完了,再次运行 CircuitbreakerTest.java,如下图,这一次,每个请求都会打印出此时断路器的状态:


知识点小结

  • 至此,用于观测断路器状态的自定义过滤器就算完成了,整个过程还是有不少知识点的,咱们来盘点一下:


  1. 常规的局部过滤器开发步骤

  2. 过滤器执行顺序的逻辑

  3. spring 的依赖注入和自动装配

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
Spring Cloud Gateway自定义过滤器实战(观测断路器状态变化)