Spring Cloud Gateway 自定义过滤器实战 (观测断路器状态变化)
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
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,
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
predicates:
Path=/hello/**
filters:
name: CircuitBreakerStatePrinter
name: CircuitBreaker
args:
name: myCircuitBreaker
改完了,再次运行 CircuitbreakerTest.java,如下图,这一次,每个请求都会打印出此时断路器的状态:
知识点小结
至此,用于观测断路器状态的自定义过滤器就算完成了,整个过程还是有不少知识点的,咱们来盘点一下:
常规的局部过滤器开发步骤
过滤器执行顺序的逻辑
spring 的依赖注入和自动装配
评论