1.崩潰的一天
12月20號,算得上西安崩潰的一天。
12月19號新增病例21個,20號新增病例42個,並且有部分病例已經在社群內傳播...
西安防疫壓力巨大,各單位公司要求,需48小時核酸檢測報告上班。
在這樣嚴峻的情況下,作為防控最核心的系統:西安一碼通竟然崩潰了,並且崩潰得是那麼的徹底。
足足癱瘓超過 15+ 個小時!
整整一天的時間呀,多少上班族被堵在地鐵口,多少旅客被凍在半路上,進退不能...
到了下午,新聞甚至提示:
為了減輕系統壓力,建議廣大市民非必要不展碼、亮碼,在出現系統卡頓時,請耐心等待,儘量避免反覆重新整理,也感謝廣大市民朋友們的理解配合。
這是解決問題的方法嗎?
如果真的需要限流來防止系統崩潰,用技術手段來限流是不是會更簡單一些,甚至前面加一個 nginx 就能解決的問題。
今天,我們就試著分析一下這個業務、以及對應的技術問題。
2.產品分析
西安一碼通其它業務我們暫且不分析,那並不是重點,並且當天也沒有完全崩潰,崩潰的僅有掃碼功能。
其實這是一個非常典型的大量查詢、少數更新的業務,閉著眼睛分析一下,可以說, 90% 以上的流量都是查詢。
我們先來看看第一版的產品形態,掃碼之後展示個人部分姓名和身份證資訊,同時下面展示綠、黃、紅碼。
這是西安一碼通最開始的樣子,業務流程僅僅只需要一個請求,甚至一個查詢的 SQL 就可以搞定。
到了後來,這個介面做了2次比較大的改版。
第一次改版新增了疫苗接種資訊,加了一個邊框;第二次改版新增了核酸檢測資訊,在最下方展示核酸檢測時間、結果。
整個頁面增加了2個查詢業務,如果系統背後使用的是關聯式資料庫,可能會多增加至少2個查詢SQL。
基本上就是這樣的一個需求,據統計西安有1300萬人口,按照最大10%的市民同時掃碼(我懷疑不會有這麼多),也就是百萬的併發量。
這樣一個併發量的業務,在網際網路公司很常見,甚至比這個複雜的場景也多了去了。
那怎麼就崩了呢?
3.技術分析
在當天晚上的官方回覆中,我們看到有這樣一句話:
12月20日早7:40分左右,西安“一碼通”使用者訪問量激增,每秒訪問量達到以往峰值的10倍以上,造成網路擁塞,致使包括“一碼通”在內的部分應用系統無法正常使用。“
一碼通”後臺監控第一時間報警,各24小時駐場通訊、網路、政務雲、安全和運維團隊立即開展排查,平臺應用系統和資料庫執行正常,判斷問題出現在網路介面側。
根據上面的資訊,資料庫和平臺系統都正常,是網路出現了問題。
我之前在文章《一次dns快取引發的慘案》畫過一張訪問示意圖,用這個圖來和大家分析一下,網路出現問題的情況。
一般使用者的請求,會先從域名開始,經過DNS伺服器解析後拿到外網IP地址,經過外網IP訪問防火牆和負載之後打到伺服器,最後伺服器響應後將結果返回到瀏覽器。
如果真的是網路出現問題,一般最常見的問題就是 DNS 解析錯誤,或者外網的寬頻被打滿了。
DNS解析錯誤一定不是本次的問題,不然可能不只是這一個功能出錯了;外網的寬頻被打滿,直接增加頻寬就行,不至於一天都沒搞定。
如果真的是網路側出現問題,一般也不需要改動業務,但實際上系統恢復的時候,大家都發現介面回到文章開頭提到了第一個版本了。
也就是說系統“回滾”了。
介面少了接種資訊和核酸檢測資訊的內容,並且在一碼通的首頁位置,新增加了一個核酸查詢的頁面。
所以,僅僅是網路介面側出現問題嗎?我這裡有一點點的疑問。
4.個人分析
根據我以往的經驗,這是一個很典型的系統過載現象,也就是說短期內請求量超過伺服器響應。
說人話就是,外部請求量超過了系統的最大處理能力。
當然了,系統最大處理能力和系統架構息息相關,同樣的伺服器不同的架構,系統負載量差異極大。
應對這樣的問題,解決起來無非有兩個方案,一個是限流,另外一個就是擴容了。
限流就是把使用者擋在外面,先處理能處理的請求;擴容就是加伺服器、增加資料庫承載能力。
上面提到官方讓大家沒事別刷一碼通,也算是人工限流的一種方式;不過在技術體系上基本上不會這樣做。
技術上的限流方案有很多,但最簡單的就是前面掛一個 Nginx 配置一下就能用;複雜一點就是接入層自己寫演算法。
當然了限流不能真正的解決問題,只是負責把一部分請求擋在外面;真正解決問題還是需要擴容,滿足所有使用者。
但實際上,根據解決問題的處理和產品回滾的情況來看,一碼通並沒有第一時間做擴容,而是選擇了回滾。
這說明,在系統架構設計上,沒有充分考慮擴容的情況,所以並不能支援第一時間選擇這個方案。
5.理想的方案?
上面說那麼多也僅僅是個人推測,實際上可能他們會面臨更多現實問題,比如工期緊張、老闆控制預算等等...
話說回來,如果你是負責一碼通公司的架構師,你會怎麼設計整個技術方案呢?歡迎大家留言,這裡說說我的想法。
第一步,讀寫分離、快取。
至少把系統分為2大塊,滿足日常使用的讀業務單獨抽取出來,用於承接外部的最大流量。
單獨抽出一個子系統負責業務的更新,比如接種資訊的更新、核酸資訊的變化、或者根據業務定時變更碼的顏色。
同時針對使用者大量的單查詢,上快取系統,優先讀取快取系統的資訊,防止壓垮後面的資料庫。
第二步,分庫分表、服務拆分。
其實使用者和使用者之間的單個查詢是沒有關係的,完全可以根據使用者的屬性做分庫分表。
比如就用使用者ID取模分64個表,甚至可以分成64個子系統來查詢,在介面最前端將流量分發掉,減輕單個表或者服務壓力。
上面分析沒有及時擴容,可能就是沒有做服務拆分,如果都是單個的業務子服務的話,遇到過載的問題很容易做擴容。
當然,如果條件合適的話,上微服務架構就更好了,有一套解決方案來處理類似的問題。
第三步,大資料系統、容災。
如果在一個頁面中展示很多資訊,還有一個技術方案,就是通過非同步的資料清洗,整合到 nosql 的一張大表中。
使用者掃描查詢等相關業務,直接走 nosql 資料庫即可。
這樣處理的好處是,哪怕更新業務完全掛了,也不會影響使用者掃碼查詢,因為兩套系統、資料庫都是完全分開的。
使用異地雙機房等形式部署服務,同時做好整體的容災、備災方案,避免出現極端情況,比如機房光纜挖斷等。
還有很多細節上的優化,這裡就不一一說明了,這裡也只是我的一些想法,歡迎大家留言補充。
6.最後
不管怎麼分析,這肯定是人禍而不是天災。
系統在沒有經過嚴格測試之下,就直接投入到生產,在強度稍微大一點的環境中就崩潰了。
比西安大的城市很多,比西安現在疫情還要嚴重的情況,其它城市也遇到過,怎麼沒有出現類似的問題?
西安做為一個科技大省,出現這樣的問題真的不應該,特別是我看了這個小程式背後使用的域名地址之後。
有一種無力吐槽的感覺,雖然說這和程式使用沒有關係,但是從細節真的可以看出一個技術團隊的實力。
希望這次能夠吸取教訓,避免再次出現類似的問題!