当前位置: 首页 > news >正文

还在手写重试逻辑?一篇搞定重试工具(附实战案例)

一、背景与痛点

在复杂的分布式系统或与外部服务交互的应用中,临时性故障(Transient Fault)是常态。例如:

  • 数据库连接因网络抖动而失败

  • HTTP请求远程服务超时

  • 读取的文件暂时被占用

如果在业务代码中直接编写重试逻辑,会导致代码臃肿、重复、难以维护。为了解决这一问题,设计并实现了一个通用的重试工具类RetryUtil,支持自定义重试次数、重试间隔以及重试条件,处理需要重试的场景。

二、核心实现:通用重试工具类 RetryUtil

RetryUtil工具类将提供一个通用的重试方法,支持自定义重试次数、重试间隔、重试条件等。可以用于多种需要重试的场景。

通用的重试工具类RetryUtil

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

importjava.util.concurrent.TimeUnit;

importjava.util.function.Supplier;

/**

* 重试工具类

* @title RetryUtil

* @author pingping_ye

* @date 2025-03-14 16:24

*/

publicclassRetryUtil {

/**

* 通用重试方法

* @param <T> 返回值类型

* @param supplier 要执行的逻辑(返回值为 T 的 Supplier)

* @param maxRetries 最大重试次数

* @param retryInterval 重试间隔(单位:秒)

* @param retryCondition 重试条件(如果返回 true,则继续重试)

* @return 执行结果

*/

publicstatic<T> T retry(

Supplier<T> supplier,

intmaxRetries,

intretryInterval,

java.util.function.Predicate<Exception> retryCondition) {

intretryCount =0;

while(retryCount <= maxRetries) {

try{

returnsupplier.get();// 执行业务逻辑

}catch(Exception e) {

retryCount++;

if(retryCount > maxRetries || !retryCondition.test(e)) {

thrownewRuntimeException("重试失败,达到最大重试次数或不符合重试条件", e);

}

try{

TimeUnit.SECONDS.sleep(retryInterval);// 等待重试间隔

}catch(InterruptedException ie) {

Thread.currentThread().interrupt();

thrownewRuntimeException("线程中断异常", ie);

}

}

}

thrownewIllegalStateException("重试逻辑异常,未正确执行");

}

}

三、工具类的优势

  • 通用性RetryUtil类可以用于任何需要重试逻辑的场景,只需传入具体的业务逻辑和重试条件。

  • 灵活性:可以通过参数配置重试次数、重试间隔和重试条件。

  • 简洁性:将重试逻辑封装在一个工具类中,避免在业务代码中重复编写重试逻辑。

四、实战案例:微信二维码获取接口的重试改造

改造前:重试逻辑与业务代码耦合

业务方法改造前

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

publicWxQrCode getQrCode(WeChatOfficialIdpScanCodeConfig config) {

MpAccessToken mpAccessToken =this.getDynamoDbWxAccessToken();

GetQRCodeParam getQRCodeParam =newGetQRCodeParam();

String request = JsonUtil.toJson(getQRCodeParam);

//注意点一

String response = HttpUtil.post(String.format(WX_OFFICIAL_QRCODE_URL, mpAccessToken.getAccess_token()), request);

//判断有没有token过期

String errCode = JsonUtil.getContext().parse(response).read("$.errcode", String.class);

if(ObjectUtil.isNotNull(errCode) &&"42001".equals(errCode) ||"40001".equals(errCode)) {

mpAccessToken =this.getDynamoDbWxAccessToken();

//注意点二,这个是坏味道的代码实现

response = HttpUtil.post(String.format(WX_OFFICIAL_QRCODE_URL, mpAccessToken.getAccess_token()), request);

}

checkResponse(response);

returnJsonUtil.toBean(response, WxQrCode.class);

}

问题:重试逻辑只处理了token过期的情况,且只能重试一次;代码中混杂了业务逻辑和重试控制。

改造后:使用 RetryUtil 分离关注点

业务方法改造后

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

publicWxQrCode getTicket(WeChatOfficialIdpScanCodeConfig config) {

returnRetryUtil.retry(()->{

WeChatService weChatService = SpringContext.getBean(WeChatService.class);

String accessToken = weChatService.getAccessToken(config.getClientId(), config.getClientSecret());

if(StrUtil.isBlank(accessToken)) {

thrownewRuntimeException("请求生成带参数的二维码异常,accessToken为空");

}

GetQRCodeParam getQRCodeParam =newGetQRCodeParam();

String request = JsonUtil.toJson(getQRCodeParam);

String response = HttpUtil.post(String.format(WX_OFFICIAL_QRCODE_URL, accessToken), request);

if(StrUtil.isBlank(response)) {

thrownewRuntimeException("请求生成带参数的二维码异常,响应报文为空");

}

String errCode = JsonUtil.getContext().parse(response).read("$.errcode", String.class);

if("42001".equals(errCode) ||"40001".equals(errCode)) {

thrownewRuntimeException("请求生成带参数的二维码异常,接口响应错误码errCode"+errCode);

}

returnJsonUtil.toBean(response, WxQrCode.class);

},3,1,e -> einstanceofRuntimeException);

}

改进点

  • 逻辑清晰:业务代码只关注"做什么",重试控制由工具类负责。

  • 可配置性强:重试次数、间隔、条件一目了然。

  • 易于扩展:未来如果需要修改重试策略,只需调整参数,无需改动核心业务。

其他场景-重试HTTP请求

1

2

3

4

5

6

7

8

publicString fetchHttpData(String url) {

returnRetryUtil.retry(

() -> HttpUtil.get(url),// 要执行的逻辑

3,// 最大重试次数

2,// 重试间隔(秒)

e -> einstanceofIOException// 重试条件:只重试 IOException

);

}

其他场景-重试数据库查询

1

2

3

4

5

6

7

8

publicUser getUserById(intuserId) {

returnRetryUtil.retry(

() -> userRepository.findById(userId),// 要执行的逻辑

5,// 最大重试次数

1,// 重试间隔(秒)

e -> einstanceofSQLException// 重试条件:只重试 SQLException

);

}

五、注意事项

  1. 幂等性:被重试的业务逻辑必须是幂等的,否则多次执行可能导致数据不一致。

  2. 异常类型:重试条件应精确匹配预期的"临时性异常",避免将业务错误(如参数校验失败)也进行重试。

  3. 线程中断:工具类正确处理了InterruptedException,保留了线程的中断状态,符合Java并发最佳实践。

  4. 死循环风险:注意while(true)的退出条件,当前实现通过retryCount > maxRetries保证一定会退出。

六、RetryUtil vs 专业重试框架

依赖大小无(纯JDK)~100KB~200KB
学习成本极低中等中等
功能丰富度基础丰富(注解+模板)丰富(熔断+限流+重试)
适用场景小型项目、简单重试Spring生态项目微服务、云原生应用

当前工具类功能基础,可以扩展支持更复杂的重试场景。

http://www.jsqmd.com/news/506230/

相关文章:

  • 零基础学Arcgis(九)| 坐标系实战:从北京54到CGCS2000的精准转换
  • ResNet中的残差块和跳连接:为什么它们能让神经网络训练得更深?
  • 支付宝红包套装避坑指南:别再为了核销花冤枉钱 - 团团收购物卡回收
  • Halcon图像预处理实战:5种滤波方法对比与工业缺陷检测案例
  • 分析2026年湖南工具磨床制造商,自动化工具磨床推荐哪家 - 工业设备
  • python日志logging、django日志等
  • MCP与OAuth 2026深度集成方案(2026 Q2强制升级倒计时·仅剩90天)
  • 2026年香港身份规划机构费用大揭秘,性价比高的有哪些? - myqiye
  • Omron NJ NX程序:精准控制机器人与伺服轴模组,集成EtherCAT总线网络节点与触摸...
  • FPGA开发实战:CORDIC IP核在三角函数计算中的高效应用
  • Qwen3-Embedding-0.6B结合Dify:打造智能问答机器人实战
  • 你的frpc服务真的稳了吗?除了开机自启,这些守护和监控技巧也得会
  • VXLAN与EVPN深度解析:为什么现代云网络都在用这种组合?
  • Z-Image-Turbo-辉夜巫女实战:Python入门者也能玩转AI图像生成
  • 从Claude Code到多模态:GME-Qwen2-VL-2B在代码生成场景的扩展应用
  • WGS84坐标转换实战:5分钟搞定C++与Matlab互转(附完整代码)
  • Phi-3-vision-128k-instruct 技能拓展:创建自定义视觉 Skills 智能体
  • 告别爬虫封号风险:用wxauto合法监控微信群消息并存入MySQL的实战指南
  • 告别论文焦虑,超实用毕业神器推荐
  • 破解微信网页版访问难题:wechat-need-web实现跨环境稳定访问的技术方案与应用价值
  • PLECS仿真入门:手把手教你搭建离网并联逆变器下垂控制模型(附功率均分调试技巧)
  • 【开题答辩全过程】以 高效便捷的民航订票系统为例,包含答辩的问题和答案
  • 保姆级教程:用Peach Fuzzer 3.1.124给Modbus Slave软件‘找茬’,成功挖到0day
  • 仅限TOP 5%嵌入式团队掌握的C语言固件溯源技术:符号级依赖图谱构建+跨版本ABI一致性校验流程
  • 创业公司的“客户投诉多”?Agentic AI+提示工程的智能投诉处理方案
  • AI应用架构师的企业AI平台运营秘诀:6个数据驱动技巧,让平台ROI提升70%
  • 99%成功率:3步破解百度网盘资源获取难题
  • Qwen3-Reranker-4B多语言混合排序展示:中英混杂内容处理
  • Vivado时序约束实战指南 ----基准时钟、生成时钟与虚拟时钟的精准配置
  • 2026年济南豪华车维修哪家靠谱?德系专修、汽车保养、故障诊断工作室选择指南 - 海棠依旧大