有人做了一次横向测试,对比 Ky、ofetch、Wretch 这类 fetch 封装在请求中途被中断时的行为差异。
为什么这个测试有价值
重试是韧性设计里最基础的一环,但也是最容易写错的一环。真实世界中的失败模式比想象中丰富:连接超时、读取超时、服务端 5xx、限流 429、以及用户在请求进行中主动取消。
最后这一类尤其容易被忽略:用户点了取消,库要能正确把 abort 信号传递下去,而不是继续重试。
测试揭示了什么
不同库在处理中断时的行为确实不一致。有些库能正确区分「用户主动取消」和「网络失败」,前者不该重试;有些会把取消也当成失败重试一遍。
这个差异在实践中的后果:用户取消了操作,界面已经跳转,后台还在重试请求,甚至重试成功写入了数据。
几类必须区分的错误
用户主动取消:绝不重试。
连接建立失败:通常可以重试,但要控制次数。
超时:可重试,但要考虑幂等性。
4xx(除 429):绝不重试,客户端错误重试没有意义。
429:可重试,且应该遵循 Retry-After 头。
5xx:可重试,配合指数退避和抖动。
幂等性是关键约束
GET 重试是安全的。POST 重试要慎重——如果服务端没有幂等键机制,重试可能造成重复下单、重复扣款。
实践中应该只对明确幂等的请求启用重试,或者由调用方提供 Idempotency-Key。
退避策略
固定间隔重试会造成重试风暴:服务刚恢复就被一大波重试打垮。
指数退避加随机抖动是标准做法。抖动不能省——没有抖动的话,所有客户端会在同一时刻重试。
给使用者的建议
第一,明确你的库在 abort 时的行为,写个测试覆盖它。
第二,默认关闭重试,只对特定请求开启。全局开启重试是很多诡异问题的来源。
第三,设置总超时。重试次数乘以单次超时可能远超用户能接受的时间,要有整体上界。
第四,重试要打日志并上报指标。重试率突然升高通常是上游出问题的早期信号。
本文由奇幻维度内容助手整理发布,欢迎在评论区交流你的看法。