崩潰的一天,西安一碼通崩潰背后的技術(shù)問題。

1
崩潰的一天
12月20號,算得上西安崩潰的一天。
12月19號新增病例21個,20號新增病例42個,并且有部分病例已經(jīng)在社區(qū)內(nèi)傳播...
西安防疫壓力巨大,各單位公司要求,需48小時核酸檢測報告上班。
在這樣嚴(yán)峻的情況下,作為防控最核心的系統(tǒng):西安一碼通竟然崩潰了,并且崩潰得是那么的徹底。
足足癱瘓超過 15+ 個小時!
整整一天的時間呀,多少上班族被堵在地鐵口,多少旅客被凍在半路上,進退不能...
到了下午,新聞甚至提示:
為了減輕系統(tǒng)壓力,建議廣大市民非必要不展碼、亮碼,在出現(xiàn)系統(tǒng)卡頓時,請耐心等待,盡量避免反復(fù)刷新,也感謝廣大市民朋友們的理解配合。
這是解決問題的方法嗎?
如果真的需要限流來防止系統(tǒng)崩潰,用技術(shù)手段來限流是不是會更簡單一些,甚至前面加一個 nginx 就能解決的問題。
今天,我們就試著分析一下這個業(yè)務(wù)、以及對應(yīng)的技術(shù)問題。
2
產(chǎn)品分析
西安一碼通其它業(yè)務(wù)我們暫且不分析,那并不是重點,并且當(dāng)天也沒有完全崩潰,崩潰的僅有掃碼功能。
其實這是一個非常典型的大量查詢、少數(shù)更新的業(yè)務(wù),閉著眼睛分析一下,可以說, 90% 以上的流量都是查詢。
我們先來看看第一版的產(chǎn)品形態(tài),掃碼之后展示個人部分姓名和身份證信息,同時下面展示綠、黃、紅碼。

這是西安一碼通最開始的樣子,業(yè)務(wù)流程僅僅只需要一個請求,甚至一個查詢的 SQL 就可以搞定。
到了后來,這個界面做了2次比較大的改版。
第一次改版新增了疫苗接種信息,加了一個邊框;第二次改版新增了核酸檢測信息,在最下方展示核酸檢測時間、結(jié)果。

整個頁面增加了2個查詢業(yè)務(wù),如果系統(tǒng)背后使用的是關(guān)系數(shù)據(jù)庫,可能會多增加至少2個查詢SQL。
基本上就是這樣的一個需求,據(jù)統(tǒng)計西安有1300萬人口,按照最大10%的市民同時掃碼(我懷疑不會有這么多),也就是百萬的并發(fā)量。
這樣一個并發(fā)量的業(yè)務(wù),在互聯(lián)網(wǎng)公司很常見,甚至比這個復(fù)雜的場景也多了去了。
那怎么就崩了呢?
3
技術(shù)分析
在當(dāng)天晚上的官方回復(fù)中,我們看到有這樣一句話:
12月20日早7:40分左右,西安“一碼通”用戶訪問量激增,每秒訪問量達到以往峰值的10倍以上,造成網(wǎng)絡(luò)擁塞,致使包括“一碼通”在內(nèi)的部分應(yīng)用系統(tǒng)無法正常使用?!?/p>
一碼通”后臺監(jiān)控第一時間報警,各24小時駐場通信、網(wǎng)絡(luò)、政務(wù)云、安全和運維團隊立即開展排查,平臺應(yīng)用系統(tǒng)和數(shù)據(jù)庫運行正常,判斷問題出現(xiàn)在網(wǎng)絡(luò)接口側(cè)。
根據(jù)上面的信息,數(shù)據(jù)庫和平臺系統(tǒng)都正常,是網(wǎng)絡(luò)出現(xiàn)了問題。
我之前在文章《一次dns緩存引發(fā)的慘案》畫過一張訪問示意圖,用這個圖來和大家分析一下,網(wǎng)絡(luò)出現(xiàn)問題的情況。

一般用戶的請求,會先從域名開始,經(jīng)過DNS服務(wù)器解析后拿到外網(wǎng)IP地址,經(jīng)過外網(wǎng)IP訪問防火墻和負(fù)載之后打到服務(wù)器,最后服務(wù)器響應(yīng)后將結(jié)果返回到瀏覽器。
如果真的是網(wǎng)絡(luò)出現(xiàn)問題,一般最常見的問題就是 DNS 解析錯誤,或者外網(wǎng)的寬帶被打滿了。
DNS解析錯誤一定不是本次的問題,不然可能不只是這一個功能出錯了;外網(wǎng)的寬帶被打滿,直接增加帶寬就行,不至于一天都沒搞定。
如果真的是網(wǎng)絡(luò)側(cè)出現(xiàn)問題,一般也不需要改動業(yè)務(wù),但實際上系統(tǒng)恢復(fù)的時候,大家都發(fā)現(xiàn)界面回到文章開頭提到了第一個版本了。

也就是說系統(tǒng)“回滾”了。
界面少了接種信息和核酸檢測信息的內(nèi)容,并且在一碼通的首頁位置,新增加了一個核酸查詢的頁面。

所以,僅僅是網(wǎng)絡(luò)接口側(cè)出現(xiàn)問題嗎?我這里有一點點的疑問。
4
個人分析
根據(jù)我以往的經(jīng)驗,這是一個很典型的系統(tǒng)過載現(xiàn)象,也就是說短期內(nèi)請求量超過服務(wù)器響應(yīng)。
說人話就是,外部請求量超過了系統(tǒng)的最大處理能力。
當(dāng)然了,系統(tǒng)最大處理能力和系統(tǒng)架構(gòu)息息相關(guān),同樣的服務(wù)器不同的架構(gòu),系統(tǒng)負(fù)載量差異極大。
應(yīng)對這樣的問題,解決起來無非有兩個方案,一個是限流,另外一個就是擴容了。
限流就是把用戶擋在外面,先處理能處理的請求;擴容就是加服務(wù)器、增加數(shù)據(jù)庫承載能力。
上面提到官方讓大家沒事別刷一碼通,也算是人工限流的一種方式;不過在技術(shù)體系上基本上不會這樣做。
技術(shù)上的限流方案有很多,但最簡單的就是前面掛一個 Nginx 配置一下就能用;復(fù)雜一點就是接入層自己寫算法。
當(dāng)然了限流不能真正的解決問題,只是負(fù)責(zé)把一部分請求擋在外面;真正解決問題還是需要擴容,滿足所有用戶。
但實際上,根據(jù)解決問題的處理和產(chǎn)品回滾的情況來看,一碼通并沒有第一時間做擴容,而是選擇了回滾。
這說明,在系統(tǒng)架構(gòu)設(shè)計上,沒有充分考慮擴容的情況,所以并不能支持第一時間選擇這個方案。
5
理想的方案?
上面說那么多也僅僅是個人推測,實際上可能他們會面臨更多現(xiàn)實問題,比如工期緊張、老板控制預(yù)算等等...
話說回來,如果你是負(fù)責(zé)一碼通公司的架構(gòu)師,你會怎么設(shè)計整個技術(shù)方案呢?歡迎大家留言,這里說說我的想法。
第一步,讀寫分離、緩存。
至少把系統(tǒng)分為2大塊,滿足日常使用的讀業(yè)務(wù)單獨抽取出來,用于承接外部的最大流量。
單獨抽出一個子系統(tǒng)負(fù)責(zé)業(yè)務(wù)的更新,比如接種信息的更新、核酸信息的變化、或者根據(jù)業(yè)務(wù)定時變更碼的顏色。
同時針對用戶大量的單查詢,上緩存系統(tǒng),優(yōu)先讀取緩存系統(tǒng)的信息,防止壓垮后面的數(shù)據(jù)庫。
第二步,分庫分表、服務(wù)拆分。
其實用戶和用戶之間的單個查詢是沒有關(guān)系的,完全可以根據(jù)用戶的屬性做分庫分表。
比如就用用戶ID取模分64個表,甚至可以分成64個子系統(tǒng)來查詢,在接口最前端將流量分發(fā)掉,減輕單個表或者服務(wù)壓力。
上面分析沒有及時擴容,可能就是沒有做服務(wù)拆分,如果都是單個的業(yè)務(wù)子服務(wù)的話,遇到過載的問題很容易做擴容。
當(dāng)然,如果條件合適的話,上微服務(wù)架構(gòu)就更好了,有一套解決方案來處理類似的問題。
第三步,大數(shù)據(jù)系統(tǒng)、容災(zāi)。
如果在一個頁面中展示很多信息,還有一個技術(shù)方案,就是通過異步的數(shù)據(jù)清洗,整合到 nosql 的一張大表中。
用戶掃描查詢等相關(guān)業(yè)務(wù),直接走 nosql 數(shù)據(jù)庫即可。
這樣處理的好處是,哪怕更新業(yè)務(wù)完全掛了,也不會影響用戶掃碼查詢,因為兩套系統(tǒng)、數(shù)據(jù)庫都是完全分開的。
使用異地雙機房等形式部署服務(wù),同時做好整體的容災(zāi)、備災(zāi)方案,避免出現(xiàn)極端情況,比如機房光纜挖斷等。
還有很多細(xì)節(jié)上的優(yōu)化,這里就不一一說明了,這里也只是我的一些想法,歡迎大家留言補充。
6
最后
不管怎么分析,這肯定是人禍而不是天災(zāi)。
系統(tǒng)在沒有經(jīng)過嚴(yán)格測試之下,就直接投入到生產(chǎn),在強度稍微大一點的環(huán)境中就崩潰了。
比西安大的城市很多,比西安現(xiàn)在疫情還要嚴(yán)重的情況,其它城市也遇到過,怎么沒有出現(xiàn)類似的問題?
西安做為一個科技重鎮(zhèn),出現(xiàn)這樣的問題真的不應(yīng)該,特別是我看了這個小程序背后使用的域名地址之后。

有一種無力吐槽的感覺,雖然說這和程序使用沒有關(guān)系,但是從細(xì)節(jié)真的可以看出一個技術(shù)團隊的實力。
希望這次能夠吸取教訓(xùn),避免再次出現(xiàn)類似的問題!
1、TikTok新直播軟件被曝疑似違規(guī)使用OBS源碼
2、顛覆你的認(rèn)知,你知道emoji表情包也可以注冊成一個域名嗎?
3、他們花19萬辦了一場元宇宙夢幻婚禮!90后青年又能大省一筆
5、XShell?收費?5款免費且超贊的SSH工具,一個比一個香!

