7行代碼讓B站崩潰3小時,竟因“一個詭計多端的0”


一個小小字符“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

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”。

在某種發佈模式中,應用的實例權重會短暫地調整為0,此時註冊中心返回給SLB (負載均衡) 的權重是字符串類型的“0”。此發佈環境隻有生產環境會用到,同時使用的頻率極低,在SLB前期灰度過程中未觸發此問題。

SLB在balance_by_lua階段,會將共享內存中保存的服務IP、Port、Weight作為參數傳給lua-resty-balancer模塊用於選擇upstream server,在節點weight=“0”時,balancer模塊中的_gcd函數收到的入參b可能為“0”。

以“事後諸葛亮”的視角來看,這個引發B站全面崩潰的根本原因多少有點讓人直呼“就這”。

但從當事程序員的視角來看,事情確實沒有辣麼簡單。

當天晚上22:52分——大部分程序員才剛下班或者還沒下班的節骨眼 (doge),B站運維收到服務不可用的報警,第一時間懷疑機房、網絡、四層LB、七層SLB等基礎設施出現問題。

然後立馬和相關技術人員拉個緊急語音會議開始處理。

5分鐘後,運維發現承載全部在線業務的主機房七層SLB的 CPU占用率達到100%,無法處理用戶請求,排除其他設施後,鎖定故障為該層。

(七層SLB是指基於URL等應用層信息的負載均衡。負載均衡通過算法把客戶請求分配到服務器集群,從而減少服務器壓力。)

萬般緊急之時,小插曲還現:遠程在傢的程序員登上VPN卻沒法進入內網,隻好又去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


相關推薦

2023-03-06

,根據後來官方的解答,服務器崩潰是因為當時新上線的代碼函數存在問題,導致服務器故障。因此,也有觀點認為,此次B站崩”,也有可能是更新迭代時的代碼存在問題。不過,截止目前,B站官方尚未對此做出回應,預計正

2022-07-20

篇文章,好傢夥,讓整個 B 站崩潰的原因,竟然隻是一行代碼沒寫好???借著這篇文章,世超準備帶大傢從 B 站的角度來回顧一下這件事情。放心,不會有生澀難懂的名詞,不會有犀利糊塗的黑話,保證小白也能看明白。&

2022-08-13

麼多,怎麼還被陰陽怪氣。這一切都得從那一行“消失的代碼”說起。一行代碼引發的慘案簡單來說,英特爾就是少敲一行用來把光線追蹤數據轉移到顯卡顯存上的代碼。在沒有這麼一行代碼的情況下,Vulkan驅動會將光線追蹤數

2024-03-09

在拜登7日晚發表國情咨文前,美國前總統特朗普曾預告,他將在自己的社交媒體平臺“真相社交”上對拜登的演講進行“實時評論”。這引發美國政界和媒體廣泛關註,但事件走向卻令特朗普尷尬。法新社稱,“真相社交”網

2022-08-30

程序員一天能寫多少行代碼?前不久,CSDN發佈的《2021-2022中國開發者調查報告》顯示,大部分程序員平均每天會寫200行左右的代碼。那代碼的數量能衡量一個程序員的水平嗎?在此前的一起勞動爭議案件中,一科技公司的算法

2022-10-23

一樣沉重。前奧迪研發主管Peter Mertens曾透露,ID.3的1000萬行代碼中,沒有一行來自大眾工程師。他還嘲諷稱,“到2020年,世界上最強大車企研發部門造出的汽車僅相當於特斯拉2012年產出的汽車。”在大眾集團用ID電動車痛擊對

2022-11-03

訓練調優等一站式服務,在線0代碼就可體驗模型效果,1行代碼實現模型推理,10行代碼實現模型調優和定制。平臺還提供在線開發功能和算力支持,無需任何安裝部署,打開網頁就可以開發AI模型。據介紹,魔搭社區堅持中立開

2024-09-11

最近,一名12歲的男孩小明因持續高燒和頭痛,在雲南的醫院接受7天的治療,但癥狀並未得到緩解。他甚至出現腹痛、嘔吐、精神狀態不佳,以及呼吸衰竭的嚴重情況。緊急之下,小明被轉移到深圳寶安區婦幼保健院的兒童重癥

2022-08-25

高達2萬美元的學生貸款債務後,美國政府的財政援助網站崩潰。根據Twitter、Downdetector.com上的一些用戶報告以及TheVerge工作人員的測試,人們在登錄StudentAid.gov時遇到問題,甚至根本無法加載該網站。周三宣佈的計劃並沒有提供

2024-02-18

換算成超過70萬單詞,或1小時視頻、11小時音頻、超過3萬行代碼。沒錯,這些數據模態Gemini 1.5都已經內建支持。從今天起,開發者和客戶就可以在Vertex API或AI Studio申請試用。剛剛收到消息還在震驚中的網友們 be like:還有人直

2023-11-07

11月7日消息,浙江交警公佈一起高速錯過路口,強行變道致多車追尾的事故。在G25杭新景高速建金樞紐入口處,一輛黑色SUV在第一車道突然減速變道,後方三輛小車避讓不及發生追尾。據解,黑色SUV駕駛者因電話接入造成導航信

2022-11-06

代碼量?華為公佈數據顯示,這三年來他們就多寫1904萬行代碼。在日前的HDC華為開發者大會上, 華為終端BG軟件部總裁龔體公佈一些數據,2019年的Harmony OS(鴻蒙)1.0版本隻有492萬行自研代碼。即便如此,龔體稱這已經是華為整

2024-02-16

輕易地與數十萬字的超長文檔、擁有數百個文件的數十萬行代碼庫、一部完整的電影等等進行交互。同時,為介紹這款劃時代的模型,Google還發佈長達58頁的技術報告。論文地址:https://storage.googleapis.com/deepmind-media/gemini/gemini_v1_5

2023-03-07

馬斯克決定不再免費開放這一API,要改成收費模式。那這代碼不就得跟著改嘛,然鵝,負責這事兒的竟然有且僅有一位工程師。The Verge直接不留情面地說:這都是馬斯克大幅裁員的鍋,Twitter現在工程師太少。而網友們更是嘲諷