伙伴云客服论坛»论坛 S区 S零代码 查看内容

17 评论

0 收藏

分享

7行代码让B站解体3小时,竟因“一个诡计多端的0”

鱼羊 丰色 发自 凹非寺
量子位 | 公众号 QbitAI
一个小小字符“0”,竟引得B站全面解体。


7行代码让B站解体3小时,竟因“一个诡计多端的0”-1.jpg

不知你是否还记得那一夜,B站“大楼停电”、“效劳器爆炸”、“程序员删库跑路”的彻夜狂欢。(手动狗头)

时隔一年,背后“真凶”如今终于被阿B披露出来——


7行代码让B站解体3小时,竟因“一个诡计多端的0”-2.jpg

没想到吧,就是这么简单几行代码,直接干趴B站两三个小时,搞得B站程序员彻夜无眠头发狂掉。

你可能会问,这不就是个普普通通用来求最大公约数的函数吗,怎么就有如此大的威力?

背后一桩桩一件件,归根结底其实就一句话:0,它真的不兴除啊。


7行代码让B站解体3小时,竟因“一个诡计多端的0”-3.jpg

详细详情,咱们还是一起来看看“事故报告”。

字符串“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

这个判断语句生效,结果就算出来了。

基于这样的数学原理,我们再来看这段代码,似乎没什么问题:


7行代码让B站解体3小时,竟因“一个诡计多端的0”-4.jpg

但假设输入的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%,别的用户恳求自然就处置不了了。


7行代码让B站解体3小时,竟因“一个诡计多端的0”-5.jpg

那么问题来了,这个“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等应用层信息的负载平衡。负载平衡通过算法把客户恳求分配到效劳器集群,从而减少效劳器压力。)

万般紧急之时,小插曲还现了:远程在家的程序员登上VPN却没法进入内网,只好又去call了一遍内网负责人,走了个绿色通道才全部上线(因为其中一个域名是由故障的SLB代理的)


7行代码让B站解体3小时,竟因“一个诡计多端的0”-6.jpg

此时已经过去了25分钟,抢修正式开端。

首先,运维先热重启了一遍SLB,未恢复;然后尝试回绝用户流量冷重启SLB,CPU仍然100%,还是未恢复。

接着,运维发现多活机房SLB恳求大量超时,但CPU未过载,正准备重启多活机房SLB时,内部群反响主站效劳已恢复,视频播放、推荐、评论、动态等功能已根本正常。

此时是23点23分,间隔事故发生31分钟

值得一提的是,这些功能恢复其实是事发之时被网友们吐槽的“高可用容灾架构”发挥了作用。


7行代码让B站解体3小时,竟因“一个诡计多端的0”-7.jpg

至于这道防线为啥一开端没发挥作用,里头可能还有你我一点锅。

简单来说,就是大家伙点不开B站就开端疯狂刷新,CDN流量回源重试 + 用户重试,直接让B站流量突增4倍以上,连接数突增100倍到千万级别,多活SLB就给整过载了。


7行代码让B站解体3小时,竟因“一个诡计多端的0”-8.jpg

不过,并不是所有效劳都搞了多活架构,至此事情并没完全处置。

接下来的半个小时里,大家做了很多操作,回滚了最近两周左右上线的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层、在分发过程出问题,它还不像在后台出问题很快能重启处置。


7行代码让B站解体3小时,竟因“一个诡计多端的0”-9.jpg

为了防止这种情况发生, 有人认为要慎用递归,硬要用还是设置一个计数器,到达一个业务不太可能到达的值后直接return掉。

还有人认为这不怪递归,主要还是弱类型语言的锅。

以此还导致了“诡计多端的‘0’”这一打趣的说法。


7行代码让B站解体3小时,竟因“一个诡计多端的0”-10.jpg

另外,由于事故实在是耽搁了太久、太多事儿,当时B站给所有用户补了一天大会员。

有人就在此算了一笔账,称就是这7行代码,让b站老板一下亏了大约1,5750,0000元。(手动狗头)


7行代码让B站解体3小时,竟因“一个诡计多端的0”-11.jpg

对于这个bug,你有什么想吐槽的?

参考链接:

[1]《2021.07.13 我们是这样崩的》by 哔哩哔哩技术
https://mp.weixin.qq.com/s/nGtC5lBX_Iaj57HIdXq3Qg

— 完 —
量子位 QbitAI · 头条号签约
关注我们,第一时间获知前沿科技动态

回复

举报 使用道具

相关帖子
全部回复 (17)
查看全部
我一个文科生为什么要从头看到尾呢?看完一脸懵,也不晓得说的什么,反正就是看完了

举报 回复 支持 反对 使用道具

呵呵,虽为理工女,但学过的那点和计算机相关的知识都还给教师了,毕业超越十年了,只从事过和硬件电路相关的工作,已经做了超越五年的家庭主妇了……[捂脸][捂脸][捂脸][捂脸]也不明白我为什么有勇气点进来看,还从头看到尾,大约只想证明自己还认得字[呲牙][呲牙][呲牙][呲牙]

举报 回复 支持 反对 使用道具

程序员通病 一旦呈现意料之外的bug,就会去疑心 内核 底层 或则虚拟机的问题。最后结果发现 其实还是自己的问题

举报 回复 支持 反对 使用道具

有意思。想起1995年当foxpro编程教师,给新学员炫技就是用if语句编一个循环,必需输入正确密码才干跳出循环。于是开机后画面就是密码输入栏,学员没密码就不能用,只能等教师过来“解围”。然后就调出程序开端给学员讲解语句的原理

举报 回复 支持 反对 使用道具

这要是c语言就没这事,这肯定不是递归的锅

举报 回复 支持 反对 使用道具

递归没有意外退出的程序员就该被吊打

举报 回复 支持 反对 使用道具

写这行代码的程序员被开除了没有

举报 回复 支持 反对 使用道具

说真话,我觉得我的技术不是教师教的,也不是我自学的,而是我带的徒弟教的,他动了一个他自己都不晓得的代码,一次又一次帮他背锅之后自我升华了

举报 回复 支持 反对 使用道具

这种代码review能过就代表了B站的水平

举报 回复 支持 反对 使用道具

本版积分规则 高级模式
B Color Image Link Quote Code Smilies

一身傲气王
注册会员
主题 13
回复 18
粉丝 0
|网站地图
快速回复 返回顶部 返回列表