本文几乎与此次故障无关,它只是写本文的缘起。
周五早上发一则朋友圈,呼应一下周四下午广东电信的故障:
我在第一时间(2 点 15 左右)发觉问题,随后我怀疑欠费,马上充值 200 块,未恢复,再次充值 100 块…4 点 20 左右恢复后,我接连收到了好几条充值短信。
虽然电信网络崩了,但计费系统还好,不然也不会充值成功,但如果很多人一起充值,打一波 burst,计费系统也难扛,网络恢复后,电信又集中发送故障期间憋住的短信,这波 burst 又对短信网关造成冲击…如果不幸,突然的网络故障将连带着几个其它服务故障。这是个普遍问题,背后的社会学原因也简单。
如果系统出了问题,用户的处理方案往往高度一致,这种全部同步往往引起连锁反应。比如发现火情,人们会选择逃跑,这导致了疏散通道拥塞,引发潜在的踩踏。类似的,如果电话打不出去,首先会想到欠费,于是引发充值 burst。这些其实和 error retry 没本质区别,而系统崩溃往往都由此引发,而不是故障本身。
我在朋友圈回复一位朋友,另一个和 error retry 类似的行为是 log record。
系统发生故障时,一般会记录 error log,往往所有关联组件都会记录 log,这导致系统甚至网络被 log 阻塞,这还不包括组织 log 字符串处理没玩好造成段错误的事,所以我说系统不是故障打崩的,是记日志记崩的。你是不是也经历过系统阻塞住,反应很慢,结果发现问题在 /dev/log,或者 rsyslogd 把 CPU 打爆。
既然 reaction 可能引发正反馈,那就待在原地什么都不做。所以 Linux 的 panic 和 Windows 的 BSoD(blue screen of death) 是个好方法,日本人的地震自救与此类似。
另外就是指数退避,这非常重要。朋友圈正文说,TCP 固然遵守了该原则,但只能在 TCP 内闭环,应用往往不遵守,出了问题不停 retry,最终引发雪崩。
第三个要点是随机因子,像共享以太网和 Wi-Fi 这种物理链路设施,下面再没有为其兜底的层,它们自身要把容错做到极致,同时采用指数退避和随机因子。
类似注册类业务,充值计费以及后续发短信这种多打一,一打多场景,要引入随机因子,不能一起干。
我经常说 IDC incast 需引入随机因子解决而不是去搞什么拥塞控制算法,因为 incast 本质上后知后觉,无法事先预测,无论 loss-based,delay-based 算法,都有局部性和滞后性,无法事前应对 incast。很简单,incast 业务在 response 前随机等待一个事先配置的不大于 RTT 的时间即可。 多打一的 response 因随机时间在不同时间进入 buffer,大大减少 buffer 占用,缓解 incast 丢包,而 incast 最恐怖后果就是丢包,这大概率引发密集 retry,后果是什么,前面说过了。
朋友圈正文中 ACK 过多问题,我另有详述,这里指出拥塞控制相关的点。
乱序是 TCP 的一个诟病点,但很少有人感受过它带来的关于 ACK 过多的副作用。合理但不经常的场景,TCP 报文两两倒序到达,这直接取消 delayed ack 以及 lro/gro,data/ack 达 1:1,包量加倍。设备预留了资源处理 data,却没有预留有余量的资源处理 “由这些 data 引发的 ack”,越丢包,ACK 越多,设备压力越大,引发连锁反应。不要试图模拟和推导,没有一定体量,雪崩没那么容易,出了问题感受就是了。
电信与互联网两个阵营一直为微信等保活 app 要不要为它们 keepalive 引发的底层信令风暴而买单,在互联网内部有个类似的事,短连接业务要不要为 TCP syn/fin/rst 风暴买单。对短连接,设备处理的绝大多数报文都是 syn/fin/rst 等小控制包,特别是 NAT 等需要重处理的网关,这些小控制包本身让网关不堪重负,遇到 error retry,几乎肯定把网关打挂,而 error retry 的 root cause 往往在目标服务,与网络无关。因此有了连接复用。回到信令风暴的解法,电信侧提供 API,微信调 API 宣称 “我要一直发 keepalive,请勿让底层连接休眠。”
…
控制流和数据流不能相互影响,这都是场面话,所有雪崩问题的根本解法还是要增加资源,但事情另一面很少有人提起。增加资源之外,还要加大设施密度。最好的例子是 3G,4G,5G 的基站密度越来越大,靓货靠实物来堆。
密度越小越脆弱,雪崩影响越大。可以这么简单理解,同样多的资源,抗雪崩能力取决于分布,低密度设施单点需要强支撑,它跨了全垮了,高密度设施则施力更分散,更能容错。和基站同样好的例子是 CDN,节点密度和雪崩负相关。说了那么久 “控制爆炸半径”,不能光造词,举个例子,这就是。
典型的通用 case,稀疏集群,每台服务器负载都高,逻辑距离远,一台挂掉,临近服务器将被导入加倍流量,一旦也垮掉,故障将加速蔓延,就像丝袜脱丝一样一发不可收拾,密度高就没这问题,每台服务器负载均不太大,且逻辑距离近,负载被周边分担吸收,这是显然的道理。
…
还有一类雪崩是好心办坏事引发。参考这篇:IPv6 Linux 实现中一个路由缓存的问题,大概意思是,Linux 为 IPv6 构建了一棵路由 cache 树,用来在海量五元组 session 中快速找到匹配的 session,但这棵树的 CRUD 需要持锁,局面变成了,session 越多,性能越差。
类似的 case,也可以从各类垃圾回收机制中找到。
周四下午广东电信严重故障导致我停机 2 个小时。下班路上思考了一下相关。抛开事实不谈,从纯技术上看,雪崩确实是难搞的。零零散散写一篇随笔。
浙江温州皮鞋湿,下雨进水不会胖。