7 行代码让B站崩溃 3 小时,竟因“一个诡计多端的 0”
感谢小编网友 Sancu 的线索投递!
一个小小字符“0”,竟引得B站全面崩溃。

不知你是否还记得那一夜,B站“大楼停电”“服务器爆炸”“程序员删库跑路”的彻夜狂欢。(手动狗头)
时隔一年,背后“真凶”现在终于被阿 B 披露出来 ——

没想到吧,就是这么简单几行代码,直接干趴B站两三个小时,搞得B站程序员彻夜无眠头发狂掉。
你可能会问,这不就是个普普通通用来求最大公约数的函数吗,怎么就有如此大的威力?背后一桩桩一件件,归根结底其实就一句话:0,它真的不兴除啊。

具体详情,咱们还是一起来看看“事故报告”。
字符串“0”引发的“血案”
先来说道说道引发惨案的根本原因,也就是开头贴出的这个 gcd 函数。学过一点编程知识的小伙伴应该都知道,这是一种用辗转相除法来计算最大公约数的递归函数。
跟我们手算最大公约数的方法不同,这个算法是这样的:
举个简单的例子,a=24,b=18,求 a 和 b 的最大公约数;
a 除以 b,得到的余数是 6,那么就让 a=18,b=6,然后接着往下算;
18 除以 6,这回余数是 0,那么 6 也就是 24 和 18 的最大公约数了。
也就是说,a 和 b 反复相除取余数,直到 b=0,函数中:
if b==0 then return a end
这个判断语句生效,结果就算出来了。基于这样的数学原理,我们再来看这段代码,似乎没什么问题:

但如果输入的 b 是个字符串“0”呢?
B站的技术解析文章中提到,这段出事的代码是用 Lua 写的。Lua 具有这么几个特点:
这是一种动态类型语言,常用习惯里变量不需要定义类型,直接给变量赋值就行。Lua 在对一个数字字符串进行算术操作时,会尝试将这个数字字符串转成一个数字。在 Lua 语言中,数学运算 n%0 的结果是 nan (Not A Number)。
我们来模拟一下这个过程:
1、当 b 是一个字符串“0”时,由于这个 gcd 函数没有对其进行类型校验,因此在碰上判定语句时,“0”不等于 0,代码中“return _gcd (b, a% b)”触发,返回_gcd (“0”, nan)。
2、_gcd (“0”, nan) 再次被执行,于是返回值变成了_gcd (nan, nan)。
这下就完犊子了,判定语句中 b=0 的条件永远没法达到,于是,死循环出现了。也就是说,这个程序开始疯狂地原地转圈,并且为了一个永远得不到的结果,把 CPU 占了个 100%,别的用户请求自然就处理不了了。

那么问题来了,这个“0”它到底是怎么进去的呢?
官方说法是:
在某种发布模式中,应用的实例权重会短暂地调整为 0,此时注册中心返回给 SLB(负载均衡)的权重是字符串类型的“0”。此发布环境只有生产环境会用到,同时使用的频率极低,在 SLB 前期灰度过程中未触发此问题。SLB 在 balance_by_lua 阶段,会将共享内存中保存的服务 IP、Port、Weight 作为参数传给 lua-resty-balancer 模块用于选择 upstream server,在节点 weight=“0”时,balancer 模块中的_gcd 函数收到的入参 b 可能为“0”。
bug 是如何定位的
以“事后诸葛亮”的视角来看,这个引发B站全面崩溃的根本原因多少有点让人直呼“就这”。但从当事程序员的视角来看,事情确实没有辣么简单。
当天晚上 22:52 分 —— 大部分程序员才刚下班或者还没下班的节骨眼(doge),B站运维收到服务不可用的报警,第一时间怀疑机房、网络、四层 LB、七层 SLB 等基础设施出现问题。
然后立马和相关技术人员拉了个紧急语音会议开始处理。5 分钟后,运维发现承载全部在线业务的主机房七层 SLB 的 CPU 占用率达到了 100%,无法处理用户请求,排除其他设施后,锁定故障为该层。
(七层 SLB 是指基于 URL 等应用层信息的负载均衡。负载均衡通过算法把客户请求分配到服务器集群,从而减少服务器压力。)
万般紧急之时,小插曲还现了:远程在家的程序员没法进入内网,只好又去 call 了一遍内网负责人,走了个绿色通道才全部上线(因为其中一个域名是由故障的 SLB 代理的)。

此时已经过去了 25 分钟,抢修正式开始。
首先,运维先热重启了一遍 SLB,未恢复;然后尝试拒绝用户流量冷重启 SLB,CPU 依然 100%,还是未恢复。
接着,运维发现多活机房 SLB 请求大量超时,但 CPU 未过载,正准备重启多活机房 SLB 时,内部群反应主站服务已恢复,视频播放、推荐、评论、动态等功能已基本正常。
此时是 23 点 23 分,距离事故发生 31 分钟。
值得一提的是,这些功能恢复其实是事发之时被网友们吐槽的“高可用容灾架构”发挥了作用。

至于这道防线为啥一开始没发挥作用,里头可能还有你我一点锅。
简单来说,就是大家伙点不开B站就开始疯狂刷新,CDN 流量回源重试 + 用户重试,直接让B站流量突增 4 倍以上,连接数突增 100 倍到千万级别,多活 SLB 就给整过载了。

不过,并不是所有服务都搞了多活架构,至此事情并没完全解决。接下来的半个小时里,大家做了很多操作,回滚了最近两周左右上线的 Lua 代码,都没把剩余的服务恢复。
时间来到了 12 点,没有办法了,“先不管 bug 是怎么出来的,把服务全恢复了再说”。简单 + 粗暴:运维直接耗时一小时重建了一组全新的 SLB 集群。
凌晨 1 点,新集群终于建好:一边,有人负责陆续将直播、电商、漫画、支付等核心业务流量切换到新集群,恢复全部服务(凌晨 1 点 50 分全部搞定,暂时结束了崩了逼近 3 个小时的事故);
另一边,继续分析 bug 原因。在他们用分析工具跑出一份详细的火焰图数据后,那个搞事的“0”才终于露出了一点端倪:CPU 热点明显集中在一个对 lua-resty-balancer 模块的调用中。而该模块的_gcd 函数在某次执行后返回了一个预期外的值:NaN。
同时,他们也发现了触发诱因的条件:某个容器 IP 的 weight=0。他们怀疑是该函数触发了 jit 编译器的某个 bug,运行出错陷入死循环导致 SLB CPU 100%。于是就全局关闭了 jit 编译,暂时规避了风险。一切都解决完后,已经快 4 点,大家终于暂时睡了个好觉。
第二天大家也没闲着,马不停蹄地在线下环境复现了 bug 后,发现并不是 jit 编译器的问题,而是服务的某种特殊发布模式会出现容器实例权重为 0 的情况,而这个 0 是个字符串形式。
正如前面所说,这个字符串“0”在动态语言 Lua 中的算术操作中,被转成了数字,走到了不该走的分支,造成了死循环,引发了 b 站此次前所未见的大崩溃事件。
递归的锅还是弱类型语言的锅?
不少网友都还对这次事故记忆犹新,有回想起自己就是以为手机不行换电脑也不行的,也有人还记得当时 5 分钟后此事就上了热搜。
大家都很诧异,就这么一个简单的死循环就能造成如此大的网站崩服。不过,有人指出,死循环不罕见,罕见的是在 SLB 层、在分发过程出问题,它还不像在后台出问题很快能重启解决。

为了避免这种情况发生,有人认为要慎用递归,硬要用还是设置一个计数器,达到一个业务不太可能达到的值后直接 return 掉。
还有人认为这不怪递归,主要还是弱类型语言的锅。以此还导致了“诡计多端的‘0’”这一打趣的说法。

另外,由于事故实在是耽误了太久、太多事儿,当时B站给所有用户补了一天大会员。
有人就在此算了一笔账,称就是这 7 行代码,让 b 站老板一下亏了大约 1,5750,0000 元。(手动狗头)

对于这个 bug,你有什么想吐槽的?
参考链接:
[1]《2021.07.13 我们是这样崩的》by 哔哩哔哩技术
https://mp.weixin.qq.com/s/nGtC5lBX_Iaj57HIdXq3Qg
相关文章
- 重要提醒!微软终止 Windows Vista 代码库支持
- Win11 用户安装 12 月累积更新遇 0x800f0991 错误
- 0x80070424、0x80070643无法更新怎么办?有什么好的解决方法?
- win10系统提示代码0x800f0982更新失败怎么办?
- 0xt000000f错误代码怎么解决?0xt000000f错误代码解决方法
- win10蓝屏错误代码CRITICAL_STRUCTURE_CORRUPTION
- win10邮件显示代码怎么办?win10邮件显示代码解决方法
- win7出现0x80070002错误代码怎么解决?win70x80070002错误解决办法
- win10蓝屏终止代码driver怎么办_win10蓝屏终止代码driver解决教程
- win10商店下载出现错误代码0x80D02017解决方法?win10错误代码0x80D02017如何处理
系统下载排行榜71011xp
【纯净之家】Windows7 64位 全新纯净版
2番茄花园 Win7 64位 快速稳定版
3【纯净之家】Windows7 32位 全新纯净版
4【纯净之家】Win7 64位 Office2007 办公旗舰版
5【雨林木风】Windows7 64位 装机旗舰版
6【纯净之家】Windows7 SP1 32位 全补丁旗舰版
7【电脑公司】Windows7 64位 免费旗舰版
8JUJUMAO Win7 64位旗舰纯净版
9【深度技术】Windows7 64位 官方旗舰版
10【游戏专用】Windows7 64位 装机旗舰版
【纯净之家】Windows10 22H2 64位 游戏优化版
2【纯净之家】Windows10 22H2 64位 专业工作站版
3【纯净之家】Windows10 企业版 LTSC 2021
4【纯净之家】Windows10企业版LTSC 2021 纯净版
5【深度技术】Windows10 64位 专业精简版
6【纯净之家】Windows10 22H2 64位 企业版
7【雨林木风】Windows10 64位 专业精简版
8JUJUMAO Win10 X64 极速精简版
9【深度技术】 Win10 64位国庆特别版
10【纯净之家】Windows10 32位 官方正式版
【纯净之家】Windows11 23H2 64位 游戏优化版
2【纯净之家】Windows11 23H2 64位 专业工作站版
3【纯净之家】Windows11 23H2 64位 纯净专业版
4风林火山Windows11下载中文版(24H2)
5【纯净之家】Windows11 23H2 64位 企业版
6【纯净之家】Windows11 23H2 64位 中文家庭版
7JUJUMAO Win11 24H2 64位 专业版镜像
8【纯净之家】Windows11 23H2 64位专业精简版
9【纯净之家】Windows11 23H2 64位 纯净家庭版
10JUJUMAO Win11 24H2 64位 精简版镜像
深度技术 GHOST XP SP3 电脑专用版 V2017.03
2深度技术 GGHOST XP SP3 电脑专用版 V2017.02
3萝卜家园 GHOST XP SP3 万能装机版 V2017.03
4Win7系统下载 PCOS技术Ghost WinXP SP3 2017 夏季装机版
5番茄花园 GHOST XP SP3 极速体验版 V2017.03
6雨林木风 GHOST XP SP3 官方旗舰版 V2017.03
7电脑公司 GHOST XP SP3 经典旗舰版 V2017.03
8雨林木风GHOST XP SP3完美纯净版【V201710】已激活
9萝卜家园 GHOST XP SP3 完美装机版 V2016.10
10雨林木风 GHOST XP SP3 精英装机版 V2017.04
热门教程
装机必备 更多+
重装工具