重开?!也可能是故障放大器- -!

简介: 重试是稳定性关键,但非万能:盲目重试会放大故障、引发雪崩。真正有效的重试需判断失败是否可恢复、是否幂等、是否加重压,并配合指数退避+抖动、总超时控制与死信兜底——重试的艺术,在于知止。

做系统稳定性时,重试几乎是绕不开的配置。网络抖一下,重试。数据库超时,重试。消息确认失败,重试。

看起来很合理。

问题是,重试不是免费的。它会增加请求数量,会延长故障持续时间,还可能把一次失败变成重复写入、请求风暴,最后把原本还能自愈的系统推向雪崩。

所以,真正该问的不是“要不要重试”,而是:

这次失败,重试之后真的更可能成功吗?

一、最危险的误区:失败了就再来一次

假设有一个商品库存服务,数据库因为连接池耗尽,响应时间从几十毫秒涨到了几秒。

上游服务调用一次,超时。

于是它自动重试。

重试请求再次打到库存服务。数据库更忙,继续超时。上游再重试一次。

如果系统里有网关、业务服务和消息消费者,每一层都各自配置了 3 次重试,一次原始请求可能被放大成几十次甚至更多。

这还没算定时任务、补偿任务和人工重放。

结果呢,故障本身只需要几秒钟恢复,重试流量却让下游持续处在高压状态。数据库越来越慢,上游越来越急,更多请求进入重试队列。

这不就拉胯了么?

重试本来是为了给系统争取恢复时间,最后却变成了持续施压。

二、哪些失败适合重试?

不是所有错误都值得重试。判断标准可以简单一点:

失败原因是暂时的,且下一次请求不会造成额外副作用,才适合重试。

02_重试判断分流

1. 比较适合重试的情况

  • 网络连接被短暂中断;
  • DNS 或连接建立阶段偶发失败;
  • 下游返回 502、503、504;
  • 服务明确返回限流信号,例如 429;
  • 数据库连接池临时耗尽;
  • 消息确认阶段连接断开;
  • 依赖服务正在发布或短暂切换。

这些错误有一个共同点:问题可能会自行恢复。

但“可能恢复”不等于“立刻重试”。如果下游现在已经过载,马上再打一次,只会让它更难恢复。

2. 更适合立即失败的情况

  • 参数校验失败;
  • 身份认证失败;
  • 权限不足;
  • 资源不存在;
  • 业务规则明确不允许;
  • SQL 或请求格式错误;
  • 数据版本冲突,且没有冲突解决策略;
  • 同一个不可重复执行的写操作没有幂等保护。

比如余额扣减接口返回“余额不足”,你重试 10 次,余额也不会突然变多。

再比如用户名格式错误,重试只是把错误重复提交给服务端。这个时候应该尽快返回明确错误,而不是把线程、连接和队列都占住。

三、固定间隔,看起来简单,实际很容易撞车

最基础的重试策略是固定间隔:

失败后等待 1 秒
第 2 次失败后等待 1 秒
第 3 次失败后等待 1 秒

这个策略的问题是,所有请求的节奏都一样。

假设某个下游服务在 10:00:00 出现故障,1000 个请求同时失败。它们在 10:00:01 一起重试,还是失败。然后 10:00:02 再一起重试。

这不是恢复,是排队冲锋。

更常见的做法是指数退避:

第 1 次重试:等待 1 秒
第 2 次重试:等待 2 秒
第 3 次重试:等待 4 秒
第 4 次重试:等待 8 秒

它能明显降低短时间内的请求密度。

但只使用指数退避还不够。如果所有请求都按照同一套时间表等待,它们仍然会在 1 秒、2 秒、4 秒这些时间点集中发起。

所以还需要加抖动,也就是给等待时间增加一个随机范围:

实际等待时间 = 指数退避时间 + 随机抖动

例如本来准备等待 4 秒,可以让请求在 3 到 5 秒之间随机发起。

这样不同请求会被打散,不会在同一时刻再次撞到下游。

四、写操作能不能重试,关键看幂等

读请求重试通常比较容易理解。写请求就麻烦多了。

因为超时并不代表服务端没有执行成功。

客户端发起创建订单请求,服务端已经写入数据库,但响应还没返回,网络连接断了。客户端只看到一个超时,于是再次发送创建订单请求。

如果没有幂等控制,最后可能出现两个订单。

支付、扣库存、发优惠券、创建任务,都有类似风险。

04_幂等与重复执行

常见的幂等方式有三类

1. 幂等键

客户端为同一个业务动作生成唯一请求号,例如:

order_id = 202610210001
idempotency_key = pay_202610210001

服务端收到请求后,先检查这个幂等键是否处理过。

如果已经处理过,就直接返回第一次处理的结果,不再重复执行。

2. 唯一约束

把业务上不能重复的字段设置成唯一键。

例如一个用户对同一商品只能有一条有效收藏记录,就可以使用:

unique(user_id, product_id)

这样即使请求被重复发送,数据库也能挡住第二次写入。

当然,数据库报唯一冲突后,业务层要能识别这是“已经成功过”,而不是把它当成普通失败。

3. 状态机限制

给业务动作设计明确状态。

例如订单只能从:

待支付 → 已支付 → 已发货

向前推进。

如果订单已经是“已支付”,再次收到支付成功通知时,就不应该重复扣款,而应该返回当前状态。

这部分不能只靠客户端自觉。客户端可能超时、重启,也可能因为网络问题重复发送。幂等保护必须尽量放在服务端。

五、消息确认失败,不代表消息没处理

消息系统里有一个很容易被忽略的场景。

消费者收到消息后,已经完成了业务处理,但在发送确认之前,连接断了。

消息队列认为这条消息没有被确认,于是再次投递。

如果消费者没有去重,同一条消息就会被处理两次。

这不是消息系统“重复发错了”,而是消费过程本身就可能出现:

业务处理成功
确认消息失败
消息再次投递

所以消息消费者不能只依赖“消费成功后确认”这一个动作。常见做法包括:

  • 使用消息唯一 ID 做消费去重;
  • 在本地记录已经处理过的消息;
  • 对关键写操作增加唯一约束;
  • 给重复消费设计安全返回;
  • 超过最大重试次数后进入死信队列。

死信队列不是垃圾桶。

它应该保留失败原因、原始消息、重试次数、最后一次错误和时间信息。否则消息进去了,后面没人知道为什么失败,也没办法判断能不能人工补偿。

六、重试次数不是越多越好

很多系统喜欢把重试次数配置成 3 次、5 次,甚至更多。

但“最大重试次数”只是一个参数,不是完整策略。

至少还需要考虑三个限制:

1. 总截止时间

一个请求不能因为多次重试一直占着线程。

比如接口的整体截止时间是 10 秒,那么所有重试、等待和网络调用都必须在这 10 秒内完成。

2. 重试预算

对同一个依赖服务,要限制重试流量占全部请求的比例。

如果某个服务当前有 1000 个请求,其中 900 个都来自重试,说明系统已经不是在正常工作了。

这个时候继续重试,收益通常很低,风险却很高。

3. 最大退避时间

指数退避必须设置上限。

否则一次持续故障可能让请求等待几十分钟,调用方线程、连接和任务状态都会被长期占用。

真正可控的策略应该类似这样:

最多重试 3 次
总耗时不超过 10 秒
等待时间指数增长
每次加入随机抖动
超过截止时间立即失败

05_重试边界策略

必要时,还需要结合限流、熔断和降级。下游已经明确不可用时,先减少请求,比不断重试更有价值。

七、一个故障场景:同样的下游故障,结果可能完全不同

假设商品详情服务依赖一个数据库集群。

某天数据库主节点发生切换,持续 8 秒无法正常响应。

方案 A:固定间隔重试

每个请求失败后每隔 1 秒重试,最多重试 5 次。

结果:

  • 大量请求在同一时间点重新打到详情服务;
  • 详情服务继续占用连接等待数据库;
  • 数据库恢复后,先接收到一大批积压请求;
  • 应用线程和连接池同时承压;
  • 部分请求最终超时,重试又从上游重新开始。

数据库虽然恢复了,应用却没有恢复。

方案 B:指数退避加抖动

请求失败后,等待时间逐步增加,并加入随机抖动。超过总截止时间后直接返回可识别的失败结果。

结果:

  • 重试请求被分散到不同时间;
  • 数据库恢复后不会瞬间被打满;
  • 一部分请求可以在下游恢复后成功;
  • 另一部分请求及时失败,由上层降级或进入补偿队列;
  • 故障影响范围被控制在一个明确的时间窗口内。

这里的差别,不是“有没有重试”。

而是有没有给下游留恢复时间,有没有控制重试流量,有没有判断请求是否还能安全执行。

八、重试策略应该先回答这 6 个问题

给接口加重试前,我一般会先问:

  1. 这个错误是暂时性的,还是确定性的?
  2. 请求再次执行,会不会产生重复写入?
  3. 服务端能不能通过幂等键识别同一个业务动作?
  4. 重试会不会进一步增加下游压力?
  5. 整个请求最多允许等待多久?
  6. 超过重试上限后,失败结果交给谁处理?

如果这 6 个问题没有答案,先别急着把重试次数改成 5 次。

尤其是写操作。没有幂等保护的写请求,重试不是可靠性设计,而是在赌网络到底什么时候断。

结尾

重试的价值,是给暂时性故障留出恢复空间。

它的风险,是让每一个失败请求都继续占用系统资源。

固定间隔可能造成同步冲锋。指数退避可以降低压力,抖动可以避免请求再次撞车。幂等键解决重复写入,死信队列承接最终失败,截止时间和重试预算则负责把影响范围锁住。

所以,重试不是“失败后再来一次”这么简单。

它决定了系统遇到故障时,是慢慢恢复,还是越救越乱。

真正稳定的系统,不是永远不失败。

而是失败之后,知道什么时候再试,什么时候停手。

相关文章
|
8天前
|
云安全 人工智能 运维
当攻击全面Agent化,你的安全防御能否跟上AI Speed?
多起客户侧AK泄露事件应急的真实观察
1320 6
|
6天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
1128 21
|
2天前
|
存储 人工智能 自然语言处理
阿里云千问办公官网登录入口:qwenwork.cn网页版免下载,注册送2000积分、登录每日再送100积分
千问办公(QwenWork)是阿里巴巴推出的一站式AI办公平台,定位"不止于对话,更注重交付"。它基于千问大模型,具备任务拆解与多步骤自动执行能力,用户一句话即可完成PPT生成、数据分析、网页搭建、视频剪辑、信息调研等复杂任务,覆盖网页端、桌面端及钉钉生态。网页版免下载,访问qwenwork.cn注册即得2000积分,每日登录再送100积分。个人版免费起用,另有付费版与企业版可选。
|
11天前
|
存储 人工智能 安全
千问办公官网入口网页版登录:qwenwork.cn 注册送2000积分、登录送100积分
阿里云千问办公(QwenWork)是AI智能工作平台,支持一句话生成PPT、表格、网页、视频及数据分析。个人免费版送2000积分+每日登录赠100分,含1GB存储、5个发布页;企业版198元/席/月起。阿里千问办公QwenWork官网:https://t.aliyun.com/U/0VCTGt 阿里AI工作平台,一句话完成数据分析、PPT 生成、视频剪辑、网页搭建等复杂任务
|
6天前
|
人工智能 开发者
Qoder CN 大使计划正式开启|无限创造,等你一起
全年免费积分、优先内测、活动支持与更多共创机会,邀请用 Qoder 把想法变成现实的你。
567 6
|
1月前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
10037 33
|
7天前
|
安全 Linux 芯片
【万字长文】Ollama下载、安装、配置、使用保姆级教程(2026最新图解)
Ollama 是一款免费开源的本地大模型运行工具,支持 DeepSeek、Qwen 等主流模型,一条命令即可下载运行。2026 最新版 v0.35.1 官方安装包(1.47GB)已提供国内直连下载,纯本地、离线可用、无广告、无隐私泄露风险。
685 1
|
17天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
8天前
|
Web App开发 人工智能 JavaScript
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA
REA(Reverse Engineer Anything)是近期爆火的开源AI逆向工具,4月发布,1个月内斩获15万+ Star。它让AI Agent无需源码即可分析二进制、APK、JS/Electron、HAR包等17类目标,自动反编译、生成可运行代码,并强制返回带证据与局限的结论。全本地执行,MIT协议,支持Hopper/Ghidra/IDA等引擎,一行命令即可上手。
794 2
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA