springboot 增加消息自动重试机制 技术方案
SpringBoot 消息自动重试机制:完整技术方案(生产可用)
我给你做一套直接落地、覆盖所有场景、无坑版的 SpringBoot 消息重试方案,包含:
同步重试、异步重试、死信队列、幂等性、失败策略、代码可直接复制。
适用场景:
- MQ 消息(RabbitMQ/RocketMQ/Kafka)
- 第三方接口调用
- 定时任务失败
- 异步任务异常
- 数信系统数据同步、消息通知
一、方案总览(4 种重试机制,按场景选)
表格
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Spring Retry | 同步调用、接口重试、轻量级 | 无中间件、代码极简 | 不支持跨重启 |
| @Retryable 注解 | 业务方法、异步方法 | 零侵入、注解驱动 | 需配合 AOP |
| 死信队列 + 延时队列 | MQ 消息、高可靠场景 | 最稳定、不丢消息 | 依赖 MQ |
| 定时任务重试表 | 跨服务、持久化、可监控 | 可追踪、可管理 | 需建表 |
二、最推荐:Spring 原生注解重试(@Retryable)
最简单、最通用、生产最常用
1. 引入依赖
xml
<!-- Spring 重试核心 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<!-- AOP 必须 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 启动类开启重试
java
运行
@EnableRetry // 关键注解
@SpringBootApplication
public class MessageApplication {
}
3. 业务方法添加自动重试(核心代码)
java
运行
@Service
public class MessageService {
/**
* 消息发送/数据同步 方法
* 自动重试:最多3次,间隔2秒,出现异常重试
*/
@Retryable(
value = Exception.class, // 捕获哪些异常重试
maxAttempts = 3, // 最大重试次数
delay = 2000, // 首次重试间隔
multiplier = 2, // 间隔倍数:2s → 4s → 8s
backoff = @Backoff(delay = 2000, multiplier = 2)
)
public void sendMessage(String msgId, String content) throws Exception {
// 你的业务:消息发送、接口调用、MQ发送、数据库操作
System.out.println("尝试发送消息:" + content);
// 模拟异常
if (true) {
throw new RuntimeException("消息发送失败,触发重试");
}
}
/**
* 全部重试失败 → 进入兜底方法
*/
@Recover
public void recover(Exception e, String msgId, String content) {
System.out.println("消息重试全部失败!msgId=" + msgId);
// 1. 记录死信表
// 2. 发送告警
// 3. 人工介入
}
}
机制说明
- 调用方法抛异常 → 自动重试
- 重试次数用完 → 自动进入
@Recover降级方法 - 支持指数退避(越来越长的间隔,避免压垮服务)
三、高可靠方案:MQ 消息重试 + 死信队列(生产级)
数信系统、消息中间件场景必须用这个,不丢消息
以 RabbitMQ 为例:
流程
- 消息消费异常 → 不 ack → 自动重新入队
- 配置重试次数上限
- 超过次数 → 打入 死信队列(DLQ)
- 死信队列做人工兜底、告警、重放
核心配置(application.yml)
yaml
spring:
rabbitmq:
listener:
simple:
retry:
enabled: true # 开启消费者重试
max-attempts: 3 # 最大重试次数
initial-interval: 2000 # 初始间隔
multiplier: 2 # 指数间隔
stateless: true
acknowledge-mode: manual # 手动ack(推荐)
消费端代码
java
运行
@RabbitListener(queues = "business.queue")
@Component
public class MessageConsumer {
@RabbitHandler
public void receive(String msg, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
try {
// 业务处理
System.out.println("消费消息:" + msg);
int i = 1 / 0; // 模拟异常
// 正常确认
channel.basicAck(tag, false);
} catch (Exception e) {
// 异常 → 重试(由spring-retry管理)
// 超过次数 → 自动进入死信
}
}
}
四、最稳定方案:重试持久化表 + 定时任务(跨重启、可监控)
适合:数信系统数据同步、重要消息、必须保证不丢失
1. 建表
sql
CREATE TABLE message_retry (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
msg_id VARCHAR(64) NOT NULL COMMENT '消息ID',
content TEXT COMMENT '消息内容',
retry_count INT DEFAULT 0 COMMENT '已重试次数',
max_retry INT DEFAULT 3 COMMENT '最大重试次数',
status VARCHAR(20) DEFAULT 'INIT' COMMENT 'INIT/RETRYING/FAILED/SUCCESS',
next_retry_time DATETIME COMMENT '下次执行时间',
error_msg TEXT COMMENT '异常信息',
create_time DATETIME DEFAULT NOW(),
update_time DATETIME DEFAULT NOW()
);
2. 定时任务扫描重试(@Scheduled)
java
运行
@Service
public class MessageRetryService {
@Scheduled(fixedDelay = 10000) // 每10秒扫描
public void retryFailedMessage() {
// 1. 查询:待重试、未超过次数、时间到的消息
List<MessageRetry> list = retryMapper.selectWaitRetryList();
for (MessageRetry msg : list) {
try {
// 2. 执行业务
sendMessage(msg.getContent());
// 3. 成功 → 更新状态
msg.setStatus("SUCCESS");
retryMapper.updateById(msg);
} catch (Exception e) {
// 4. 失败 → 重试次数+1
msg.setRetryCount(msg.getRetryCount() + 1);
if (msg.getRetryCount() >= msg.getMaxRetry()) {
msg.setStatus("FAILED"); // 最终失败
}
retryMapper.updateById(msg);
}
}
}
}
优点
- 服务重启后继续重试
- 可页面查看、管理、手动重放
- 数信系统、数据同步场景首选
五、全局通用重试模板(工具类,直接复制)
java
运行
@Component
public class RetryUtil {
public <T> T execute(Supplier<T> business, int maxRetry, long delay) throws Exception {
int retry = 0;
while (retry < maxRetry) {
try {
return business.get();
} catch (Exception e) {
retry++;
System.err.println("重试第" + retry + "次");
if (retry >= maxRetry) {
throw new Exception("最终失败");
}
Thread.sleep(delay);
}
}
throw new Exception("重试失败");
}
}
六、生产必须注意的 4 个关键点
1. 幂等性(必须保证)
重试会导致重复执行,解决方案:
- 消息唯一 ID
- 数据库唯一索引
- 状态机判断
- Redis 分布式锁
2. 重试间隔必须用指数退避
2s → 4s → 8s → 停止
3. 一定要有兜底策略
重试失败后必须:
- 记录死信表
- 发送企业微信 / 邮件告警
- 支持人工重放
4. 禁止无限重试
无限重试 = 死循环 = 数据库 / MQ 打挂。
七、方案选型建议(直接照选)
- 接口调用、同步方法 → Spring Retry + @Retryable
- MQ 消息消费 → MQ 重试 + 死信队列
- 数信系统数据同步、重要消息 → 重试表 + 定时任务(最稳)
- 异步任务 → @Retryable + @Async
最终总结(最简版)
- Spring Retry:最简单,适合同步重试
- MQ 重试 + 死信:最可靠,适合消息
- 重试表 + 定时任务:最通用,适合数信系统
- 所有重试必须做幂等、必须有限制、必须有兜底
------------------------------------------------------------------------------------------------------------

浙公网安备 33010602011771号