news 2026/7/23 4:51:31

不用 @CircuitBreaker 注解,自定义 Resilience4j 熔断器 + AOP 实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
不用 @CircuitBreaker 注解,自定义 Resilience4j 熔断器 + AOP 实现

前一篇写了 基于 Feign + Resilience4j 的微服务熔断防雪崩优化方案。这次同一套系统又要加熔断,但走 Feign 那条路走不通了。

三个接口直连 HTTP 调外部数据接口,返回体用业务码表达失败,不抛异常。现成的@CircuitBreaker注解只认异常,用不上,只能自己搭一套。

一、场景

三个接口都调外部数据服务,返回统一封装:

classRemoteResult<T>{privateintcode;// 0=成功,500106=远程服务异常,其他=业务码privateTdata;...}

调用方拿到结果自己判code。HTTP 200 也可能是失败。跟前一篇 Feign 那套差在这里:

  • 上一篇走 Feign,用ErrorDecoder在解码阶段把"系统异常码"转成特殊异常抛给 Resilience4j,异常路径触发熔断。
  • 这一篇直连 HTTP,RestTemplate拿回一个对象,方法里没有异常,熔断器数不到失败次数。

Resilience4j 的@CircuitBreaker注解默认只识别异常。要让它认业务码,得走recordResult回调——一个Predicate<Object>,让你自己定义什么样的返回值算失败。所以这套方案没用注解,而是包了一层自己的装饰器。

二、类图

业务代码只调代理 Bean,注解标在代理 Bean 的方法上,切面拦截并交给底层熔断器执行。

三、调用时序

熔断打开时,切面反射从方法签名RemoteResult<T>里拿到 T,new T()包一个空的RemoteResult.success(...)作为兜底。业务代码看到的永远是同类型的返回值,不用判空、不用 catch。

四、核心代码

注解——空注解,参数全靠约定:

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceRemoteCircuitBreakerProtected{}

代理 Bean——集中管理受保护的调用,解决 AOP self-invocation 问题:

@ComponentpublicclassRemoteFacade{@ResourceprivateRemoteUtilremoteUtil;@RemoteCircuitBreakerProtectedpublicRemoteResult<RespA>queryA(ReqAreq){returnremoteUtil.queryA(req);}@RemoteCircuitBreakerProtectedpublicRemoteResult<RespB>queryB(ReqBreq){returnremoteUtil.queryB(req);}// ...}

切面——统一 fallback 反射构造:

@Around("@annotation(com.example.cb.RemoteCircuitBreakerProtected)")publicObjectaround(ProceedingJoinPointpjp){Methodmethod=((MethodSignature)pjp.getSignature()).getMethod();returnbreaker.decorateSupplier(()->proceed(pjp),e->buildFallback(method));}privateObjectbuildFallback(Methodmethod){// 从 RemoteResult<T> 拿到 TParameterizedTypetype=(ParameterizedType)method.getGenericReturnType();Class<?>dataClass=(Class<?>)type.getActualTypeArguments()[0];try{returnRemoteResult.success(dataClass.getDeclaredConstructor().newInstance());}catch(ReflectiveOperationExceptione){returnRemoteResult.success(null);}}

熔断器工厂——recordResult 定义什么样的返回值算失败:

CircuitBreakerConfigconfig=CircuitBreakerConfig.custom().failureRateThreshold(60).minimumNumberOfCalls(6).slidingWindowSize(10).waitDurationInOpenState(Duration.ofSeconds(10)).permittedNumberOfCallsInHalfOpenState(3).slowCallRateThreshold(80).slowCallDurationThreshold(Duration.ofSeconds(10))// ↓↓↓ 关键.recordResult(response->{if(responseinstanceofRemoteResult<?>r&&r.getCode()==REMOTE_SERVICE_ERROR_CODE){returntrue;// 业务码算失败}returnfalse;}).build();

五、recordResult 的边界

recordResult只处理调用正常返回、但返回值本身表示业务失败的情况。如果方法内部抛了异常(RestTemplate 超时、连接失败),Resilience4j 默认会把这次调用记为失败,recordResult根本不会被调用。

所以这套方案实际上覆盖两条失败路径:

  • 抛异常 → 走 Resilience4j 默认失败识别(超时、网络异常等)
  • 不抛异常但业务码是失败 → 走 recordResult 兜底

两条合起来才不会漏掉"HTTP 200 也可能失败"的情况。

六、小结

这套方案落到代码上就三件事:

  • recordResult负责识别业务码失败,补上"HTTP 200 也是失败"这条路径
  • AOP 切面从方法签名的泛型里反射构造 fallback,业务代码不用写 lambda
  • 代理 Bean 把受保护的调用集中起来,同时避开 AOP self-invocation 的坑

再加个其他接口,只需要往代理 Bean 加一个方法、标个注解,调用方走代理 Bean。熔断的判定阈值、fallback 语义、日志格式都不用重写。

和前一篇 Feign 方案配起来,两条外部调用形态都有解法:能拦截解码的走ErrorDecoder,直接拿到封装对象的走recordResult+ AOP。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/23 4:41:53

彩虹氚气管的发光颜色会随着时间的推移而变化吗?

核心结论先讲清楚&#xff08;针对波尔 NM9032C-S2C-GR2 彩虹氚气管&#xff09;发光的基础色相不会改变&#xff1a;橙色依旧是橙色、蓝色依旧是蓝色、绿色依旧是绿色&#xff0c;不会出现蓝管慢慢变成紫色、橙管变黄这类色移。 但是会产生两种「视觉上的观感变化」&#xff0…

作者头像 李华
网站建设 2026/7/23 4:35:30

WebGL与WebGPU实战:43个案例从基础渲染到高级优化

最近在开发WebGL/WebGPU项目时&#xff0c;经常遇到各种技术难题和性能瓶颈&#xff0c;网上资料分散且不成体系。本文整合了43个实战案例&#xff0c;覆盖从基础渲染到高级优化的完整解决方案&#xff0c;包含Three.js、Unity WebGL、百度地图集成等热门场景&#xff0c;每个案…

作者头像 李华
网站建设 2026/7/23 4:32:47

基于SLAM十四讲经典Sophus示例代码对应李代公式

本文基于SLAM十四讲经典Sophus示例代码 Sophus代码逐模块拆解&#xff1a;SO(3)、SE(3)李群李代数对应数学公式 把每一行代码和李群、李代数数学公式一一对应&#xff0c;方便学习SLAM位姿表示与扰动模型。 符号约定&#xff08;与Sophus库保持一致&#xff09; SO(3)\boldsymb…

作者头像 李华
网站建设 2026/7/23 4:31:04

VSEARCH实战指南:微生物组数据分析的高效开源解决方案

1. 项目概述&#xff1a;为什么VSEARCH值得你投入时间&#xff1f;如果你正在或即将踏入微生物组研究领域&#xff0c;无论是处理16S rRNA扩增子测序数据&#xff0c;还是ITS、18S等其他标记基因&#xff0c;数据处理的效率和准确性永远是第一道坎。几年前&#xff0c;我们可能…

作者头像 李华
网站建设 2026/7/23 4:30:55

招聘软件收费模式背后的技术栈选型:C++、Java与Rust的实战解析

1. 项目概述&#xff1a;招聘软件收费模式的技术与商业逻辑最近和几个做技术招聘的朋友聊天&#xff0c;大家普遍有个感觉&#xff1a;市面上的招聘软件&#xff0c;无论是面向企业的SaaS平台&#xff0c;还是面向开发者的垂直社区&#xff0c;收费模式越来越多样&#xff0c;但…

作者头像 李华