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

接口重试的7种常用方案! - 指南

前言

记得五年前的一个深夜,某个电商平台的订单退款接口突发异常,因为银行系统网络抖动,退款请求连续失败。

原本技术团队只是想“好心重试几次”,结果开发小哥写的重试代码竟疯狂调用了银行的退款接口 82次

最终导致用户账户重复退款,平台损失过百万。

老板在复盘会上质问:“接口重试这么基础的事,为什么还能捅出大篓子?”

大家哑口无言,因为所有人都以为只要加个 for 循环,再睡几秒就完事了……

这篇文章跟大家一起聊聊重试的7种常用方案,希望对你会有所帮助。

1 暴力轮回法

问题场景

某实习生写的用户注册短信发送接口。

在一个while循环中,重复调用第三方的发短信接口给用户发送短信。

代码如下:

public void sendSms(String phone) {
int retry = 0;
while (retry <
5) { // 无脑循环
try {
smsClient.send(phone);
break;
} catch (Exception e) {
retry++;
Thread.sleep(1000); // 固定1秒睡眠
}
}
}

事故现场

某次短信服务器出现了过载问题,导致所有请求都延迟了3秒。

这个暴力循环的代码在 0.5秒内同时发起数万次重试,直接打爆短信平台,触发了 熔断封禁,连正常请求也被拒绝。

教训

2 Spring Retry

应用场景

Spring Retry适用于中小项目,通过注解快速实现基本重试和熔断(如订单状态查询接口)。

通过声明@Retryable注解,来实现接口重试的功能。

配置示例

@Retryable(
value = {TimeoutException.class}, // 只重试超时异常
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2) // 1秒→2秒→4秒
)
public boolean queryOrderStatus(String orderId) {
return httpClient.get("/order/" + orderId);
}@Recover // 兜底回退方法
public boolean fallback() {
return false;
}

优势

  • 声明式注解:代码简洁,与业务逻辑解耦

  • 指数退避:自动拉长重试间隔

  • 熔断集成:结合 @CircuitBreaker 可快速阻断异常流量

3 Resilience4j

高阶场景

对于有些需要自定义退避算法、熔断策略和多层防护的大中型系统(如支付核心接口),我们可以使用 Resilience4j。

核心代码如下:

// 1. 重试配置:指数退避 + 随机抖动
RetryConfig retryConfig = RetryConfig.custom()
.maxAttempts(3)
.intervalFunction(IntervalFunction.ofExponentialRandomBackoff(
1000L, // 初始间隔12.0,
// 指数倍数
0.3 // 随机抖动系数
))
.retryOnException(e -> e instanceof TimeoutException)
.build();// 2. 熔断配置:错误率超50%时熔断
CircuitBreakerConfig cbConfig = CircuitBreakerConfig.custom()
.slidingWindow(10,
10, CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.failureRateThreshold(50)
.build();// 组合使用
Retry retry = Retry.of("payment", retryConfig);
CircuitBreaker cb = CircuitBreaker.of("payment", cbConfig);// 执行业务逻辑
Supplier<
Boolean> supplier = () -> paymentService.pay();
Supplier<
Boolean> decorated = Decorators.ofSupplier(supplier)
.withRetry(retry)
.withCircuitBreaker(cb)
.decorate();

效果

某电商大厂上线此方案后,支付接口 超时率下降60% ,且熔断触发频率降低近 90%

真正做到了“打不还手,骂不还口”。

4 MQ队列

适用场景

高并发、允许延时的异步场景(如物流状态同步)。

实现原理

  1. 首次请求失败后,将消息投递至 延时队列

  2. 队列根据预设的延时时间(如5秒、30秒、1分钟)重试消费

  3. 若达到最大重试次数,则转存至 死信队列(人工处理)

RocketMQ代码片段如下:

// 生产者发送延时消息
Message<
String> message = new Message();
message.setBody("订单数据");
message.setDelayTimeLevel(3); // RocketMQ预设的10秒延迟级别
rocketMQTemplate.send(message);// 消费者重试
@RocketMQMessageListener(topic = "DELAY_TOPIC")
public class DelayConsumer {
@Override
public void handleMessage(Message message) {
try {
syncLogistics(message);
} catch (Exception e) {
// 重试次数 + 1,并重新发送到更高延迟级别
resendWithDelay(message, retryCount + 1);
}
}
}

如何RocketMQ的消费者消费失败,会自动发起重试。

5 定时任务

适用场景

对于有些不需要实时反馈,允许批量处理的任务(如文件导入)的业务场景,我们可以使用定时任务。

在这里以Quartz为例。

具体代码如下:

@Scheduled(cron = "0 0/5 * * * ?") //5分钟执行
public void retryFailedTasks() {
List<FailedTask> list = failedTaskDao.listUnprocessed(5); // 查失败任务
list.forEach(task -> {
try {
retryTask(task);
task.markSuccess();
} catch (Exception e) {
task.incrRetryCount();
}
failedTaskDao.update(task);
});
}

6 两阶段提交

适用场景

对于严格保证数据一致性的场景(如资金转账),我们可以使用两阶段提交机制。

关键实现

  1. 第一阶段:记录操作流水到数据库(状态为“进行中”)

  2. 第二阶段:调用远程接口,并根据结果更新流水状态

  3. 定时补偿:扫描超时的“进行中”流水重新提交

大致代码如下:

@Transactional
public void transfer(TransferRequest req) {
// 1. 记录流水
transferRecordDao.create(req, PENDING);// 2. 调用银行接口
boolean success = bankClient.transfer(req);// 3. 更新流水状态
transferRecordDao.updateStatus(req.getId(), success ? SUCCESS : FAILED);// 4. 失败转异步重试
if (!success) {
mqTemplate.send("TRANSFER_RETRY_QUEUE", req);
}
}

7 分布式锁

应用场景

对于一些多服务实例、多线程环境的防重复提交(如秒杀)的业务场景,我们可以使用分布式锁。

这里以Redis + Lua的分布式锁为例。

代码如下:

public boolean retryWithLock(String key, int maxRetry) {
String lockKey = "api_retry_lock:" + key;
for (int i = 0; i < maxRetry; i++) {
// 尝试获取分布式锁
if (redis.setnx(lockKey, "1",
30, TimeUnit.SECONDS)) {
try {
return callApi();
} finally {
redis.delete(lockKey);
}
}
Thread.sleep(1000 * (i + 1)); // 等待释放锁
}
return false;
}

总结

重试就像机房里的灭火器——永远不希望用到它,但必须保证关键时刻能救命。

我们工作中选择哪种方案?

别只看技术潮流,而要看业务的长矛和盾牌,需要哪种配合。

最后送大家一句话:系统稳定的秘诀,是永远对重试保持敬畏。

文章转载自:苏三说技术

原文链接:接口重试的7种常用方案! - 苏三说技术 - 博客园

体验地址:JNPF快速开发平台

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

相关文章:

  • 这3种底层思维就是新老班主任的最大区别:不是经验,而
  • windows局域网,能够使用IP打开共享但无法通过机器名打开(0x80070035)
  • 笔记本 copilot按键 PowerToys映射
  • 实用指南:86-python电网可视化项目-6
  • 详细介绍:3.5mm耳机插座技术全解析:从镀层工艺到阻抗稳定性测试
  • 通过电脑调试 Android/iOS 手机端网页
  • java数据类型和转义字符
  • CMS垃圾回收器详解
  • 网页自动转发替换图片
  • 实用指南:用MATLAB画一只可爱的小熊
  • 成熟稳定、省钱好用的AI应用怎么开发?趣丸科技员工助手的技术实践
  • JavaScript 自定义元素类的作用域跨环境兼容管理
  • victoriamonitor监控gcp的cloudrun - Super
  • QT实现QTreeWidget项目拖拽移动功能
  • 解决 Semi Design Upload 组件实现自定义压缩,上传文件后无法触发 onChange
  • 实用指南:生活琐记(3)
  • 重构商业生态:全域分销商城小程序开发赋能商家高效增长 - 实践
  • 设计模式-建造者模式 - 实践
  • 自动化释放5G全部潜力:新西兰电信One NZ的实践之路
  • 实用指南:C++设计模式_创建型模式_原型模式Prototype
  • 第二十一篇
  • DEIMv2浅读
  • 阿里出手了:全免费!号称国内版ClaudeCode?
  • [MS-DOS]MS-DOS 6.22 with CD-ROM Driver.ver.6.22.English下载与安装
  • 2025 年国内品牌设计公司最新推荐排行榜:聚焦行业领军者优势,精选优质服务商深度解析
  • 报考PostgreSQL中级认证证书多少钱?
  • 087_尚硅谷_switch使用细节(1)
  • linux服务器操作系统字符集是GBK,tomcat和部署的程序是UTF-8,启动后应用界面乱码如何解决
  • 2025 年感温电缆厂家最新推荐权威榜单重磅发布,全方位解析头部品牌优势助力工业消防精准选型
  • 完整教程:2- 十大排序算法(希尔排序、计数排序、桶排序)