MQ项目接入Retry实践——构造器模式+命令模式+模版方法模式


一、为什么需要重试机制?

在实际开发中,网络请求、数据库操作、远程服务调用等场景都可能因瞬时故障 (如网络抖动、服务短暂不可用)导致失败。重试机制的核心思想是:通过有限次数的重复尝试,提高操作最终成功的概率。这就像打电话时对方占线,我们会选择稍后再拨,而不是直接放弃。


二、代码案例中的关键语法现象

让我们先观察代码片段:

java 复制代码
return Retryer.<SendResult>newInstance()
        .maxAttempt(maxAttempt)
        .callable(new Callable<SendResult>() {
            @Override
            public SendResult call() throws Exception {
                return doSend(messageId, mqMessage);
            }
        }).retryCall();
1. 泛型(Generics)
java 复制代码
Retryer.<SendResult>newInstance()
  • 作用 :通过 <SendResult> 指定泛型类型,声明 Retryer 最终返回的结果类型为 SendResult
  • 意义:泛型保证了链式调用中所有方法的类型一致性,编译器会检查每一步操作的类型是否正确,避免运行时类型转换错误。

2. 匿名内部类
java 复制代码
new Callable<SendResult>() {
    @Override
    public SendResult call() throws Exception {
        return doSend(messageId, mqMessage);
    }
}
  • 本质Callable 是一个函数式接口,定义了一个待执行的任务(类似 Runnable,但可以返回结果和抛出异常)。
  • 匿名内部类的意义 :将需要重试的核心逻辑 doSend() 封装成一个独立的可执行对象,实现了逻辑与重试策略的解耦

3. 链式调用(Fluent API)
java 复制代码
Retryer.newInstance().maxAttempt(...).callable(...).retryCall()
  • 链式调用的本质 :每个方法返回 this 对象,允许连续调用多个方法。
  • 优势
    • 代码可读性强,符合自然语言描述(例如"创建 Retryer 实例→设置最大重试次数→设置任务→执行")。
    • 避免冗长的临时变量,代码更加紧凑。

三、代码中隐含的设计模式

1. 构建器模式(Builder Pattern)
java 复制代码
Retryer.newInstance().maxAttempt(3).callable(...)
  • 模式解析
    • Retryer 类的设计者通过静态方法 newInstance() 创建一个"空"的构建器。
    • 通过链式方法(如 maxAttempt()callable())逐步填充配置参数。
    • 最终通过 retryCall() 完成对象的构建和执行。
  • 优势
    • 避免构造方法参数爆炸(例如 new Retryer(3, callable, ...) 参数过多)。
    • 允许灵活组合配置项,例如未来扩展时可以添加 retryInterval() 方法设置重试间隔。

2. 命令模式(Command Pattern)
java 复制代码
Callable<SendResult> task = new Callable<>() {
    public SendResult call() { ... }
};
  • 模式解析
    • 将具体的业务操作 doSend() 封装成一个 Callable 对象。
    • Retryer 不需要关心具体操作是什么,只需要负责执行这个命令对象,并处理重试逻辑。
  • 意义 :实现了**操作请求者(Retryer)操作执行者(doSend)**的解耦。

3. 模板方法模式(Template Method Pattern)
java 复制代码
// 伪代码:Retryer 内部实现
public class Retryer<T> {
    public T retryCall() {
        for (int i=0; i<maxAttempt; i++) {
            try {
                return callable.call();
            } catch (Exception e) {
                if (i == maxAttempt-1) throw e;
            }
        }
        throw new RetryException();
    }
}
  • 模式解析
    • retryCall() 定义了重试的固定流程(尝试执行→失败重试→达到最大次数后抛出异常)。
    • 具体的业务逻辑由 callable.call() 实现,子类(或调用方)无需修改重试流程。
  • 优势 :将可变部分(业务逻辑)和不变部分(重试流程)分离,符合开闭原则。

四、Retryer 的完整工作流程

通过上述分析,我们可以总结 Retryer 的工作流程如下:

  1. 初始化配置 :通过链式调用设置最大重试次数 (maxAttempt) 和待执行任务 (callable)。
  2. 执行任务 :调用 retryCall() 触发实际执行。
  3. 重试逻辑
    • 尝试执行 callable.call()
    • 如果成功,直接返回结果
    • 如果失败,检查是否达到最大重试次数
    • 未达到则重新执行,否则抛出异常

五、如何扩展更复杂的重试策略?

实际项目中可能需要更灵活的策略,例如:

  1. 指数退避(Exponential Backoff):失败后等待时间逐渐增加(如 1s、2s、4s)。
  2. 条件重试:仅对特定异常(如网络超时)进行重试。
  3. 熔断机制:连续失败多次后暂停重试,避免雪崩效应。

这些功能可以通过在 Retryer 中添加以下方法实现:

java 复制代码
Retryer.<SendResult>newInstance()
    .retryIfException(e -> e instanceof SocketTimeoutException) // 条件过滤
    .withBackoff(1, 10, TimeUnit.SECONDS) // 退避策略
    .withFallback(() -> defaultResult) // 降级逻辑

六、总结:Retry 机制的设计哲学

  1. 关注点分离 :重试策略和业务逻辑分离,通过 Callable 封装命令。
  2. 开闭原则:通过模板方法固定流程,开放具体实现扩展。
  3. 防御式编程:通过有限次数的重试,提高系统容错能力。
相关推荐
夏天的味道٥33 分钟前
36. Spring Boot 2.1.3.RELEASE 中实现监控信息可视化并添加邮件报警功能
java·spring boot·后端
bobz96543 分钟前
ipsec vpn over kube-ovn eip
后端
小杨4042 小时前
springboot框架四个基础核心三(actuator)
spring boot·后端·架构
yuhaiqiang2 小时前
分布式系统容错必杀技:从架构到代码,深度剖析分布式重试组件
后端
sinat_319868072 小时前
Spring Boot2.0之十 使用自定义注解、Json序列化器实现自动转换字典类型字段
spring boot·后端·json
用户37607127336603 小时前
DeepSeek R1本地私有化部署教程(Ollama+Spring AI)
后端
Rverdoser4 小时前
在 Centos7 上部署 ASP.NET 8.0 + YOLOv11 的踩坑实录
后端·yolo·asp.net
灰色人生qwer4 小时前
SpringBoot项目注入 traceId 来追踪整个请求的日志链路
java·spring boot·后端·日志·slf4j·链路追踪
我命由我123454 小时前
34.Java 阻塞队列(阻塞队列架构、阻塞队列分类、阻塞队列核心方法)
java·服务器·开发语言·后端·架构·java-ee·后端开发