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
相关文章
- b站投屏到电视怎么设置(b站投屏怎么投到电视上)
- B站宣布终止UWP版本客户端的维护
- B站停止维护UWP版客户端,推荐下载桌面端
- “二舅治好了我的精神内耗”刷屏,作者拒绝网友直播建议
- B站被指盗播 CBA 赛事,被中篮联索赔 4.06 亿元
- B站哔哩哔哩 App 全面上线“展示账号 IP 属地”功能,包括评论区
- B站上线 Hi-Res 无损音质
- B站:开展 2022 年暑期未成年人网络环境专项治理,未满 16 周岁未成年人禁止直播
- B站发布首个 UP 主定制数字藏品,包含高清版权视频
- B站:专项治理虚拟主播恶意内容及言论
- B站旗下《代号:艾塔》实机演示公布,已开放 CBT 内测招募
- B站上线防网暴页面,推出防私信骚扰、弹幕优选、一键取证功能
- B站:将逐步开放“展示账号 IP 属地”功能,预计一周后全面上线
- B站哔哩哔哩 Win11/10 桌面客户端新增视频离线缓存、连播能力
- B站、微博发布公告,呼吁用户规范使用汉字
- win10更新失败代码0x800F0805解决方案(win10更新失败代码0x800F0805怎么办)
系统下载排行榜71011xp
番茄花园Win7 64位推荐旗舰版 V2021.05
2深度技术Win7 64位豪华旗舰版 V2021.07
3番茄花园Win7 64位旗舰激活版 V2021.07
4带USB3.0驱动Win7镜像 V2021
5系统之家 Ghost Win7 64位 旗舰激活版 V2021.11
6萝卜家园Win7 64位旗舰纯净版 V2021.08
7技术员联盟Win7 64位旗舰激活版 V2021.09
8雨林木风Win7 SP1 64位旗舰版 V2021.05
9萝卜家园Ghost Win7 64位极速装机版 V2021.04
10技术员联盟Win7 64位完美装机版 V2021.04
深度技术Win10 64位优化专业版 V2021.06
2深度技术Win10系统 最新精简版 V2021.09
3Win10超级精简版 V2021
4Win10完整版原版镜像 V2021
5风林火山Win10 21H1 64位专业版 V2021.06
6Win10光盘镜像文件 V2021
7深度技术 Ghost Win10 64位 专业稳定版 V2021.11
8技术员联盟Ghost Win10 64位正式版 V2021.10
9Win10 21H1 Build 19043.1320 官方正式版
10技术员联盟Win10 64位永久激活版镜像 V2021.07
系统之家 Ghost Win11 64位 官方正式版 V2021.11
2Win11PE网络纯净版 V2021
3系统之家Ghost Win11 64位专业版 V2021.10
4Win11官网纯净版 V2021.10
5Win11 RTM版镜像 V2021
6番茄花园Win11系统64位 V2021.09 极速专业版
7Win11专业版原版镜像ISO V2021
8Win11官方中文正式版 V2021
9Win11 22494.1000预览版 V2021.11
10番茄花园Win11 64位极速优化版 V2021.08
深度技术Windows XP SP3 稳定专业版 V2021.08
2雨林木风Ghost XP Sp3纯净版 V2021.08
3萝卜家园WindowsXP Sp3专业版 V2021.06
4雨林木风WindowsXP Sp3专业版 V2021.06
5风林火山Ghost XP Sp3纯净版 V2021.08
6技术员联盟Windows XP SP3极速专业版 V2021.07
7萝卜家园 Windows Sp3 XP 经典版 V2021.04
8番茄花园WindowsXP Sp3专业版 V2021.05
9电脑公司WindowsXP Sp3专业版 V2021.05
10番茄花园 GHOST XP SP3 纯净专业版 V2021.03
热门教程 更多+
装机必备 更多+
重装教程 更多+
电脑教程专题 更多+