1、重试策略概述:为什么需要重试?
大家好,我是老张。做 DevOps 这些年,我见过太多流水线因为一次网络抖动就全盘崩溃的场景。说实话,那种凌晨三点被报警叫醒,发现只是某个服务超时导致整个部署卡死的滋味,真不好受。
今天咱们聊聊重试策略。你可能觉得这玩意儿很简单——失败了就再试一次呗。但实际落地时,坑比你想的多得多。
1.1 为什么需要重试?
先问个问题:你的流水线真的可靠吗?
我敢打赌,大部分流水线里至少有一半的失败是暂时性故障。什么叫暂时性故障?就是那种过几秒再试一次就能成功的问题。比如:
- 网络偶尔丢包,TCP 连接超时
- 数据库连接池短暂打满
- 下游服务刚好在做 GC,响应慢了 200ms
- DNS 解析偶尔失败
- 磁盘 IO 抖动导致文件写入失败
这些故障不是系统挂了,而是运气不好。你想想看,如果因为这种原因就让整个流水线失败,开发同学得重新触发构建,等个十几分钟甚至半小时,就为了等那个偶然的网络包重新传过来——这合理吗?
核心观点:重试不是掩盖问题,而是区分「暂时性故障」和「永久性故障」的第一道防线。
我在项目中遇到过最典型的例子:某次大促前,我们的部署流水线在拉取容器镜像时频繁超时。排查后发现是镜像仓库的 CDN 节点偶尔响应慢。加了一层重试后,成功率从 92% 直接飙到 99.8%。你看,问题本身没解决,但业务没受影响——这就是重试的价值。
1.2 重试的代价与收益分析
重试不是免费的午餐。每次重试都有成本,咱们得算清楚这笔账。
| 维度 | 收益 | 代价 |
|---|---|---|
| 可用性 | 提升流水线成功率 5%~30% | 增加端到端延迟(每次重试多花几百毫秒到几秒) |
| 运维成本 | 减少人工介入次数,降低 on-call 压力 | 需要设计重试逻辑,增加代码复杂度 |
| 资源消耗 | 避免因偶发故障导致的资源浪费(如已启动的构建环境) | 重试本身消耗 CPU、内存、网络带宽 |
| 下游压力 | 无直接收益 | 可能放大请求量,导致下游雪崩 |
说白了,重试策略就是在可靠性和效率之间找平衡。你重试得越多,成功率越高,但延迟和资源消耗也越大。而且——嗯,这里要注意——不加节制的重试反而会害了你。
避坑指南:我曾经见过一个团队,给所有 API 调用都加了 5 次重试,间隔 0 秒。结果下游服务一次 GC 停顿,5 个重试请求同时打过去,直接把服务打挂了。这就是典型的「好心办坏事」。
所以,重试的代价不是线性的。当重试策略设计得不好时,代价会指数级上升。你需要考虑:
- 重试次数:多少次合适?3 次是常见选择,但要看场景
- 重试间隔:立即重试还是等一会儿?固定间隔还是递增?
- 重试范围:所有失败都重试?还是只重试特定错误码?
- 幂等性:重试会不会导致数据重复?
1.3 重试策略在流水线中的位置
咱们把流水线想象成一条生产线。每个环节都可能出问题,重试策略就像生产线上的「自动返工机制」。
来看一张图,你就明白了:
从图上你能看到,重试策略不是孤立的。它嵌在流水线的每个环节里。我个人习惯把重试策略放在两个地方:
- 任务级别:每个步骤内部的重试。比如编译失败时,自动重试一次。
- 流水线级别:整个流水线的重试。比如部署失败时,回滚后重新触发整个流水线。
你想想看,如果只在任务级别重试,那某个步骤连续失败 3 次后,流水线还是会挂。如果只在流水线级别重试,那每次重试都要从头跑一遍,浪费大量时间。所以,好的策略是分层重试:
实战建议:我在项目中通常这样设计——每个任务内部做 2 次快速重试(间隔 1 秒),如果还失败,交给流水线级别的重试(最多 1 次,间隔 30 秒)。这样既覆盖了偶发故障,又不会无限重试拖垮系统。
1.4 什么时候不该重试?
聊了这么多重试的好处,也得说说它的边界。有些情况,重试是火上浇油:
- 4xx 错误:比如 401 未授权、403 禁止访问、404 不存在。这些错误重试一万次也没用,因为请求本身有问题。
- 业务逻辑错误:比如参数校验失败、数据格式不对。重试不会改变输入数据。
- 下游过载:如果下游已经返回 503 或超时,你再重试就是往火上浇油。这时候应该退避,甚至熔断。
- 非幂等操作:比如创建订单、扣减库存。重试可能导致重复扣款或重复下单。
我记得有一次,某个支付服务因为数据库主从延迟,返回了 500 错误。我们的重试策略没做错误码过滤,结果 3 次重试都成功了——但每次成功都扣了一次钱。用户收到 4 条扣款短信,那场面……嗯,从那以后我就在重试逻辑里加了个「幂等性检查」的硬性要求。
1.5 重试策略的核心要素
总结一下,设计重试策略时,你需要回答这几个问题:
| 要素 | 说明 | 常见选择 |
|---|---|---|
| 重试条件 | 哪些错误值得重试? | 5xx、网络超时、连接重置 |
| 最大次数 | 最多重试几次? | 2~5 次,视场景而定 |
| 间隔策略 | 每次重试等多久? | 固定间隔、指数退避、抖动 |
| 超时控制 | 单次重试的超时时间 | 比正常超时略短 |
| 熔断机制 | 连续失败多少次后停止重试? | 结合断路器模式 |
说白了,重试策略就是一套规则引擎。它告诉你:什么时候该坚持,什么时候该放弃。后面几章我会详细拆解每个要素的具体实现,包括代码示例和配置模板。
最后说一句:重试不是银弹。它解决的是「偶发故障」,而不是「系统缺陷」。如果你的服务每 10 次请求就失败 5 次,别想着靠重试来救——先把根因找到再说。