摘要:欢迎访问我的欢迎访问我的内容所有原创文章分类汇总及配套源码,涉及等本篇概览本篇概览本文是实战系列的第七篇,前面的文章咱们学习了各种内置过滤器,还在的断路器功能一文深入研究了断路器类型的过滤器理论实战源码分析皆有,相信聪明的您一定会有此疑问内
https://github.com/zq2599/blog_demos
内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;
本文是《Spring Cloud Gateway实战》系列的第七篇,前面的文章咱们学习了各种内置过滤器,还在《Spring Cloud Gateway的断路器(CircuitBreaker)功能》一文深入研究了断路器类型的过滤器(理论&实战&源码分析皆有),相信聪明的您一定会有此疑问:内置的再多也无法覆盖全部场景,定制才是终极武器
所以今天咱们就来开发一个自己专属的过滤器,至于此过滤器的具体功能,其实前文已埋下伏笔,如下图:
简单来说,就是在一个有断路器的Spring Cloud Gateway应用中做个自定义过滤器,在处理每个请求时把断路器的状态打印出来,这样咱们就能明明白白清清楚楚知道断路器的状态啥时候改变,变成了啥样,也算补全了《Spring Cloud Gateway的断路器(CircuitBreaker)功能》的知识点
过滤器分为全局和局部两种,这里咱们选用局部的,原因很简单:咱们的过滤器是为了观察断路器,所以不需要全局生效,只要在使用断路器的路由中生效就够了;
以上就是自定义过滤器的基本套路了,可见还是非常简单的,接下来的实战也是按照这个套路来的
在编写自定义过滤器代码之前,还有个拦路虎等着我们,也就是咱们过滤器的基本功能:如何取得断路器的状态
这个问题当然难不倒聪明的您了,没错,用反射修改此方法的访问权限,稍后的代码中咱们就这么干
还剩最后一个问题:circuitBreakerRegistry是ReactiveResilience4JCircuitBreakerFactory的成员变量,这个ReactiveResilience4JCircuitBreakerFactory从哪获取?
如果您配置过断路器,对这个ReactiveResilience4JCircuitBreakerFactory就很熟悉了,设置该对像是配置断路器的基本操作,回顾一下前文的代码:
@Configurationpublic class CustomizeCircuitBreakerConfig { @Bean public ReactiveResilience4JCircuitBreakerFactory defaultCustomizer() { CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() // .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED) // 滑动窗口的类型为时间窗口 .slidingWindowSize(10) // 时间窗口的大小为60秒 .minimumNumberOfCalls(5) // 在单位时间窗口内最少需要5次调用才能开始进行统计计算 .failureRateThreshold(50) // 在单位时间窗口内调用失败率达到50%后会启动断路器 .enableAutomaticTransitionFromOpenToHalfOpen() // 允许断路器自动由打开状态转换为半开状态 .permittedNumberOfCallsInHalfOpenState(5) // 在半开状态下允许进行正常调用的次数 .waitDurationInOpenState(Duration.ofSeconds(5)) // 断路器打开状态转换为半开状态需要等待60秒 .recordExceptions(Throwable.class) // 所有异常都当作失败来处理 .build(); ReactiveResilience4JCircuitBreakerFactory factory = new ReactiveResilience4JCircuitBreakerFactory(); factory.configureDefault(id -> new Resilience4JConfigBuilder(id) .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(200)).build()) .circuitBreakerConfig(circuitBreakerConfig).build()); return factory; }}
既然ReactiveResilience4JCircuitBreakerFactory是spring的bean,那我们在StatePrinterGatewayFilterFactory类中用Autowired注解就能随意使用了
至此,理论分析已全部完成,问题都已经解决,开始编码
名称 | 链接 | 备注 |
---|---|---|
项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
前文创建了子工程circuitbreaker-gateway,此工程已添加了断路器,现在咱们的过滤器代码就写在这个工程中是最合适的了
接下来按照套路写代码,首先是StatePrinterGatewayFilter.java,代码中有详细注释就不再啰嗦了,要注意的是getOrder方法返回值是10,这表示过滤器的执行顺序:
package com.bolingcavalry.circuitbreakergateway.filter;import io.github.resilience4j.circuitbreaker.CircuitBreaker;import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;import io.vavr.collection.Seq;import lombok.extern.slf4j.Slf4j;import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory;import org.springframework.cloud.gateway.filter.GatewayFilter;import org.springframework.cloud.gateway.filter.GatewayFilterChain;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 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 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; }}
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;@Componentpublic class StatePrinterGatewayFilterFactory extends AbstractGatewayFilterFactory
server: #服务端口 port: 8081spring: 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
有个很明显的问题,聪明睿智的您当然不会忽略:上图绿框中的连续四个响应,对应的断路器状态都没有打印出来,要知道,咱们的过滤器可是要处理每一个请求的,怎么会连续漏掉四个呢?
其实原因很容易推理出来:断路器CircuitBreaker的filter先执行,然后才是咱们的CircuitBreakerStatePrinter,而处于开启状态的断路器会直接返回错误给调用方,其后面的filter都不会执行了
那么问题来了:如何控制CircuitBreaker和CircuitBreakerStatePrinter这两个filter的顺序,让CircuitBreakerStatePrinter先执行?
CircuitBreakerStatePrinter是咱们自己写的代码,修改StatePrinterGatewayFilter.getOrder的返回值可以调整顺序,但CircuitBreaker不是咱自己的代码呀,这可如何是好?
老规矩,看看断路器的源码,前文已经分析过了,断路器最重要的代码是SpringCloudCircuitBreakerFilterFactory.apply方法,如下图红框,生成的filter是GatewayFilter接口的实现类:
回顾咱们的路由配置,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: 8081spring: 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
微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...
https://github.com/zq2599/blog_demos
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/124126.html
摘要:欢迎访问我的内容所有原创文章分类汇总及配套源码,涉及等本篇概览一起深入了解的断路器功能先聊聊理论再结合官方和大神的信息确定技术栈再动手开发,先实现再验证再趁热打铁,看看它的源码最后,回顾一下有哪些不足下一篇文章解决这些不足关于断路器下图来自欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及...
摘要:是一个相对比较新的微服务框架,年才推出的版本虽然时间最短但是相比等框架提供的全套的分布式系统解决方案。提供线程池不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务器雪崩的问题。通过互相注册的方式来进行消息同步和保证高可用。 Spring Cloud 是一个相对比较新的微服务框架,...
摘要:欢迎访问我的欢迎访问我的内容所有原创文章分类汇总及配套源码,涉及等本篇概览本篇概览作为实战系列的第五篇,是时候了解过滤器的作用了,本篇咱们一起来了解内置好的过滤器,真是种类繁多功能强大过滤器顾名思义,就是在请求头部添加指定的内容带有的完整配欢迎访问我的GitHubhttps://github.com/zq2599/blog_demos内容:所有原创文章分类汇总及配套源码,涉及Java、Doc...
摘要:服务提供者的运行机制用了双层结构来维护注册的服务信息,第一层为服务的名称,第二层为服务的实例名称。服务注册中心的运行机制为了防止服务的异常下线,会周期性的清理列表中未续约的服务。负载均衡器的基本功能维护该服务下的所有节点列表。 Spring Boot Spring Boot有什么作用 Spring Boot通过自动化的配置简化Spring原有的样板化的配置。 Spring Boo...
摘要:授权框架使第三方应用程序来获取对服务的有限访问机会。无论是通过编排资源所有者和服务之间的交互批准的资源所有者,或通过允许第三方应用程序来获取自己的访问权限。 SpringCloud打造微服务平台--概览 简述 SpringCloud是什么 Spring Boot和SpringCloud是什么关系 Spring Boot是Spring的一套快速WEB开发的脚手架,可建立独立的Sprin...
阅读 3488·2021-11-22 12:06
阅读 2433·2021-10-14 09:42
阅读 3497·2021-10-11 10:59
阅读 2817·2019-08-30 11:25
阅读 2961·2019-08-29 16:25
阅读 3077·2019-08-26 17:40
阅读 1052·2019-08-26 13:30
阅读 981·2019-08-26 11:46