什么是“过载雪崩”
想象一下,你是一家咖啡店唯一的咖啡师(主理人,手动狗头)。你的最高制作速度是每分钟2杯,也就是你的最大处理能力。
- 正常情况:顾客每分钟来1.5人,你可以轻松应对,队伍很短。
- 出问题:有一天,咖啡机变慢了,你每分钟只能做1杯,但顾客还是按原来的速度来。
- 雪崩开始:等待的顾客不耐烦了(超时),他们选择重新排队(重试)。这样,每分钟来的不仅有新顾客,还有重试的人,队伍一下子变得很长。
- 彻底崩溃:新顾客看到长队直接离开(请求失败),而你做的咖啡是为50分钟前点单的人做的,他们早就走了。你做的都是无用功!整个系统(你和你的咖啡店)的服务能力降为0。
这就是“过载雪崩”:系统因处理不过来,导致用户重试,重试请求进一步压垮系统,形成恶性循环,最终所有请求都失败。
案例
案例一
基本情况
如下图,服务A是一个单进程系统,通过套接字接收前端请求进行处理。在处理过程中,需要同步请求服务B(系统B的SLA超时时间是100ms)。前端设置用户请求的超时时间为1秒。
服务A的处理流程如下:
- 接收用户请求
- 进行本地逻辑处理
- 发送请求到后端系统B
- 等待后端系统B应答
- 应答前端用户,处理下一个请求
正常情况下的负载
- 前端请求峰值为每秒30次。
- 后端系统B的并行处理能力很强,每秒可处理超过10000次请求,绝大多数请求延迟在20ms以内。
- 进程A处理请求时,主要耗时在等待后端系统B响应,本地运算几乎不耗时。
此时,系统运行良好。由于处理延迟仅为20ms,进程A每秒最多可处理50个请求,完全能够应对用户每秒30个请求的峰值。
导火索
某天,后端系统B升级后,每个请求的处理延迟从20ms增加到50ms。虽然仍在SLA规定的100ms超时范围内,但延迟明显变长。
当用户请求达到峰值时,灾难发生了:每次操作都提示“服务器超时无响应”,整个服务变得不可用。
过载分析
- 处理能力下降:进程A的处理能力受限于后端B的响应速度。当B的响应时间变为50ms时,A的处理能力降至
1000ms / 50ms = 20 QPS。 - 流量过载:此时,30 QPS的峰值流量已经超过了A的20 QPS处理能力,失败的请求会触发客户端自动重试。
- 流量放大:重试机制导致入口流量急剧上升,实际观测到的请求速率达到正常峰值的6倍以上(约200 QPS),远超A的处理能力。
- 处理无效请求:缓冲区满后,新请求会丢弃旧请求(UDP特性)或直接被丢弃。更严重的是,进程A从缓冲区取出的每个请求,都是很久以前收到的。计算可知,处理完缓冲区积压的1000个请求需要
1000 / 20 = 50秒。这意味着A正在处理50秒前的请求,而客户端早已在1秒超时后放弃等待。系统资源全部用于处理无效请求,陷入“无用功”,对所有新请求都表现为“服务无响应”。
案例二
基本情况
正常情况下,Server单机每秒可接收峰值请求300次,处理能力为每秒1500次,延迟约10ms,系统运行正常。
导火索
在某个时刻进行秒杀活动或某天发售门票时,大量用户同时发起请求,远超后台Server的最大负载能力。
操作失败的用户会不断重试,中间系统也会自动重试,进一步增加请求量,最终导致所有用户的操作都失败。
过载分析
- 初始过载:突发流量超过了Server的QPS处理能力。
- 重试放大:请求失败后,客户端会自动重试(最多2次),使Server接收到的请求量被放大到正常流量的数倍。假设正常流量为X,重试可能带来额外的
2X 流量,甚至更高,因为中间链路的各组件也可能重试,总流量远超正常水平。
总结
- 知己:必须清楚你的系统在最差情况下每分钟能处理多少请求(最大处理能力),不能靠“运气好”估算。
- 量力而行:学会拒绝。如果队伍已经排到无法按时完成,就直接告诉新用户“现在忙不过来”,而不是接单后让人干等。
- 识别无效请求:要能发现哪些请求已经过期,比如订单时间超过15分钟的直接丢弃,不再处理。
- 前端保护后端:后端明确告知前端最大可接单量,前端不能超量下单。每个环节都要明确处理能力(SLA),层层把关。
- 尽早拒绝:像高速公路提前设拥堵提示一样,及时告知用户“前方拥堵,请绕行”,让大家早点做选择,避免全部堵死。
- 安抚用户,延缓重试:操作失败时不要让用户立即重试,可以显示“网络不佳,正在重试…”的进度条,延迟几秒再尝试。多次失败后要友好提示,避免用户频繁点击。
- 设计上避免流量高峰:不要做“整点秒杀”这种容易引发流量尖峰的设计。新功能发布要灰度上线,先让少量用户试用。
- 慎用重试:中间系统不要无脑重试失败请求,这只会加剧问题。如果必须重试,一定要严格限制频率和次数。
- 快速恢复方法:一旦雪崩发生,最快的办法是重启服务(清空队列),就像咖啡店宣布“今日已售罄,清场后重新排队”,迅速恢复秩序。
- 过载保护目标:不是保证所有请求都成功,而是确保即使在高压力下,系统仍能稳定提供最大处理能力,而不是彻底崩溃。