7 行代码搞崩溃 B 站,原因令人唏嘘!

前不久,哔哩哔哩(一般常称为 B 站)发布了一篇文章《2021.07.13 我们是这样崩的》,详细回顾了他们在 2021.07.13 晚上全站崩溃约 3 小时的至暗时刻,以及万分紧张的故障定位与恢复过程。

在那篇文章中,详细地写了定位过程、问题分析、优化和改进。在我的印象中,国内大型互联网公司在发生类似事故后,如此公开地对其进行“审查”和“还债”,实属罕见。(值得发送一个有三个链接的按钮)

[En]

In that article, the positioning process, problem analysis, optimization and improvement were written in great detail. In my impression, it is rare for large domestic Internet companies to “review” and “repay their debts” so openly after similar accidents. (it is worth sending a button with three links)

这篇文章是理工科学生很好的学习材料。我最关心的是编程语言的特点,也就是代码层面的细节。

[En]

This article is a good learning material for technical students. What I am most concerned about is the features of the programming language, that is, the details at the code level.

在关于问题根因的分析中,我们看到了罪魁祸首的 7 行代码,它是用 Lua 语言写的一个求最大公约数的函数:

7 行代码搞崩溃 B 站,原因令人唏嘘!

简单而言,这个函数预期接收的参数是两个数字(普通的数字或者字符串类型的数字,即两种类型都可以),然而,它的 if 语句却只判断了一种类型(普通数字),忽略了字符串类型的”0″。

在故障发生时,它的第二个参数传入的是字符串类型”0″而不是数字类型 0,导致 if 语句判断失效!

由于 Lua 是动态类型语言,只有在程序运行时才知道传入的参数是什么类型。这属于是所有动态类型语言的特色,在 Python、JavaScript、PHP、Ruby 等动态类型语言中,也会有同样的表现。这不是啥新鲜事物。

然而,真正该死的问题在于,Lua 还是一门 弱类型语言,它不像 Python、Ruby、Java 等 强类型语言那样,它竟支持 隐式类型转换!

在 Lua 中,数字字符串在与普通数字作算术运算时,会将字符串类型隐式地转换成数字类型,如上图所示的”a % b”,如果 b 是字符串类型的数字,那它就会被转换成数字类型!

而在 Python 这种强类型动态类型语言中,这样的转换是不可思议的,数字与字符串作算术运算,能得到的只会是报错:TypeError: unsupported operand type(s) for %: ‘int’ and ‘str’

7 行代码搞崩溃 B 站,原因令人唏嘘!

Lua 语言的这种”字符串隐式变数字”的行为,即使在大意不察觉的情况下,似乎也不会造成太大问题。在 B 站代码中,除了出事故时传的字符串”0″以外,估计它一直接收的都是其它字符串数字,一直也没出问题,显然程序员是把这当成一种便利手段了(因为不需作类型转换)。

然而,不幸的是,Lua 中还有一个特殊的”nan”,它会进一步将这一个”小小的错误”传递下去,直至传到了地老天荒不受控制的死循环里……

在大多数编程语言中,被零除是一个不可原谅的错误,这与我们在小学数学课上掌握的常识是一致的:数字零不允许作为除数

[En]

In most programming languages, dividing by zero is an unforgivable mistake, which is consistent with the common sense that we have mastered in primary school math class: * the number zero is not allowed as a divisor !*

拿出你的手机,打开你的计算器,看看它显示了什么:

[En]

Take out your phone, open your calculator and see what it says:

7 行代码搞崩溃 B 站,原因令人唏嘘!

看到了吧!不能除以0!!!

继续看看 Python 对于这种操作的反应:

7 行代码搞崩溃 B 站,原因令人唏嘘!

ZeroDivisionError 除零错误,这是在捍卫我们根深蒂固的数学常识。

那么,Lua 语言在除零操作后得到的 nan 到底是个什么东西呢?

nan 一般也被称为”NaN”,是”No a Number”的缩写,表示”不是一个数”。它来头不小,是在 1985 年的 IEEE 754 浮点数标准中首次引入的。

说白了,它也是数值类型的值,但代表的是一个“不可代表的值”。换句话说,它代表了许多非常抽象的概念。

[En]

To put it bluntly, it is also a value in a numeric type, but represents an “unrepresentable value”. In other words, it represents a number of very abstract concepts.

也许我们比较容易理解另一个抽象的数”无穷大”,因为在中学数学课上就经常接触到,而 nan 也是类似的一种特殊的数,只不过它较为少用且更难以捉摸罢了。

Python 中也有这两个数的存在,即 float(‘inf’) 表示无穷大、float(‘nan’) 表示非数。它们就像是两个黑洞,会吞噬掉任何试图前来”搭讪”的数:

7 行代码搞崩溃 B 站,原因令人唏嘘!

那么,当两个黑洞相互靠近时,谁的引力更大呢?请看下面的示例:

[En]

So who has a stronger gravitational pull when the two black holes are close to each other? Take a look at the example:

7 行代码搞崩溃 B 站,原因令人唏嘘!

看来还是 nan 的优先级更高一筹啊。

然而,尽管 Python 中有 nan,但它并不因为这个数而抛弃前文提到的常识。而同为脚本语言的 Lua 却抛弃了常识, 在出现除零这种非法操作时,它不是报错,而是得到 nan 的结果。

这样的功能简直太免费了,在某个时候可能会有用,但它也可以掩埋未知的隐患。

[En]

Such a feature is simply too free, and it may be useful at some point, but it can also bury unknown hidden dangers.

回到 B 站的问题代码,弱类型的 Lua 语言由于太过自由,它放行了字符串数字与普通数字的运算,又因为对 nan 过于自由的使用,它放行了数字除零的操作,两次的放行,使得短短几行代码一路畅行不止,一路消耗服务器资源,直到 CPU 100%,直到牵动服务集群故障,直到高可用的多活机房服务不可用,导致全站崩溃 3 小时的事故……

当然,如果写这段代码的程序员多加了一个条件判断,这起事故是完全可以避免的。从另一个角度来看,这是程序员对递归程序的终止条件处理不当,以及编程语言不能放弃的两个不守规矩的语言特征。

[En]

Of course, if the programmer who wrote this code had added one more conditional judgment, this accident could have been completely avoided. From another perspective, this is the programmer’s improper handling of the termination conditions of recursive programs and the two unruly language features that can not be abandoned to the programming language.

但是,我相信写下那段代码的程序员大概率是长期使用其它编程语言,现学现卖上手写 Lua,尽管知道 Lua 语言动态弱类型的特点,但思维习惯上仍深受其它语言影响,这才”一时失足、小河翻船”……程序员内心有苦说不出!!

短短的 7 行代码,说简单就简单,说不简单也不简单。本文就不展开说辗转相除法求最大公约数了(说来话长),单单是前面提及的隐式类型转换加上除零得 nan 的细节问题,就足够导致一场大事故了。

从 7 行问题代码中,作为吃瓜群众的我们,能得到些什么收获呢?到底是涨见识了,还是”又学废了”呢?

人生苦短,不求无 Bug,但求读者老爷们赏个一键三连吧~~~

Original: https://www.cnblogs.com/pythonista/p/16533376.html
Author: 豌豆花下猫
Title: 7 行代码搞崩溃 B 站,原因令人唏嘘!

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/498947/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球