微信後臺技術“乾貨們”帶來的啓發


本篇算是我閱讀完微信後臺技術相關的乾貨文章後得到的一些啓發,如果去年中那篇屬於技術乾貨的選擇問題,這篇大概就是選擇之後的消化吸收問題了。

循證與決策路徑

在前文中提過,循證大概是我們讀技術乾貨文章的一個原始訴求,通過分析別人走過的路徑,來撥開自己技術道路探索上的迷霧。

關於 IM 類消息應用最重要的一個技術決策就是關於消息模型,微信採用了存儲轉發模型,其具體描述如下(參考[1]):

消息被髮出後,會先在後臺臨時存儲;爲使接收者能更快接收到消息,會推送消息通知給接收者;最後客戶端主動到服務器收取消息。

簡單描述這個模型就是三個步驟:

  1. 消息接收後在服務端臨時存儲,並通知發送端已發送成功。
  2. 通知接收端有消息,請來拉取。
  3. 接收端收到通知後,再來拉取真正的消息。

初一看這個模型多了一層通知再拉取的冗餘,爲什麼不直接把消息推下去?對,最早期我們自己做 IM 就設計的先嚐試直接推消息下去,若接收端沒有確認收到,再臨時存儲的模型。後者減少了臨時存儲的量和時間,也沒有一個多餘的通知。

但後面這個模型增加了另一層複雜度,在早期的 PC 互聯網時期,推送並確認效率還算挺高的,但在移動環境下,就不太行了。而且引入了移動端,實際就導致了另一層複雜性,多終端在線,多終端確認,多終端已讀和未讀,都需要在服務端記錄各個端的狀態。所以,之後我們也就慢慢演變成同時存儲和推送消息的並行模型,存儲是爲了方便各終端拉取各自的離線消息,但推送因爲需要考慮舊終端版本的支持,還得直接推消息本身而並不容易簡化成消息通知來取消掉消息的接收確認過程。

循證,即便你看到了一個更好的方式,但也要結合自身的實際情況去思考實踐的路徑。所以,如今我們的模型相比微信是一個更妥協的版本,若是五年多前要改成微信這樣的模型,也許只需要一兩個程序員一週的時間。但如今也許需要好幾個不同的開發團隊(各終端和後端)配合弄上一兩個季度也未必能將所有用戶切換乾淨了。

切磋與思考方式

IM 中還有個大家特別常用和熟悉的功能 —— 羣消息。關於羣消息模型,微信採用的是寫擴散模型,也就是說發到羣裏的一條消息會給羣裏的每個人都存一份(消息索引,參考[1])。這個模型的最大缺點就是要把消息重複很多份,通過犧牲空間來換取了每個人拉取羣消息的效率。

好多年前我們剛開始做羣時,也是採用了的寫擴散模型,後來因爲存儲壓力太大,一度又改成了讀擴散模型。在讀擴散模型下,羣消息只存一份,記錄每個羣成員讀取羣消息的偏移量(消息索引號,單調增長)。之所以存儲壓力大在於當時公司還沒有一個統一的存儲組件,我們直接採用的 Redis 的內存存儲,當時原生的 Redis 在橫向和縱向上的擴展性上都比較受限。這在當時屬於兩害相權取其輕,選擇了一個對我們研發團隊來說成本更低的方案。

再後來公司有了擴展性和性能都比較好的統一存儲組件後,實際再換回寫擴散模型則更好。畢竟讀擴散模型邏輯比較複雜,考慮自己不知道加了多少個羣了,每次打開應用都要檢查每個羣是否有消息,性能開銷是呈線程遞增的。唯一的問題是,寫好、測好、上線運行穩定幾年的程序,誰也不想再去換了對吧,每一次的技術升級和架構優化其實是需要一個契機的。

另外一個是所有分佈式後臺系統都有的共性問題 —— 性能問題。只要你的用戶量到了一定規模,比如 100 萬,以後每上一個量級,對技術支撐的挑戰實際上並不是呈線性的。微信春晚紅包的案例(參考[2])給出了一個很好的參考和啓發,因爲市面上幾乎很少有系統能到這個量級了。

微信 2015 年春節的紅包峯值請求是 1400 萬每秒,而微信後臺其實也採用了微服務的架構,其拆分原則如下(參考[1]):

實現不同業務功能的 CGI 被拆到不同 Logicsrv,同一功能但是重要程度不一樣的也進行拆分。例如,作爲核心功能的消息收發邏輯,就被拆爲 3 個服務模塊:消息同步、發文本和語音消息、發圖片和視頻消息。

服務拆散了,在自動化基礎設施的輔助下,運維效率下降不大,而開發協作效率會提升很多,但性能會下降。那麼在面對微信春晚紅包這樣的極端性能場景下,該如何應對?在電商裏,正常下單和秒殺下單多是分離的兩套系統來支撐,秒殺專爲性能優化,簡化了很多正常流程,而且秒殺本身需要支持的 sku 不多,所以它具備簡化的基礎。而微信給出的方案中實際也是類似的思路,但它有個特殊點在於,能把拆散的服務爲了性能又合併回去。

在如此海量請求之下,在這個分佈式系統中,任何一點網絡或服務的波動都可能是災難性的。最終我們選擇把搖一搖服務去掉,把一千萬每秒的請求幹掉了,把這個服務挪入到接入服務。但這樣做,有一個前提:不能降低接入服務的穩定性。因爲不單是春晚搖一搖請求,微信消息收發等基礎功能的請求也需要通過接入服務來中轉,假如嵌入的搖一搖邏輯把接入服務拖垮,將得不償失。

這裏面的黑科技在於 C++ 技術棧的優勢,同一臺接入服務器上實際由不同的進程來處理不同的功能,達到了隔離的效果。而不同進程間又可以通過共享內存來通信,這比用 Socket 網絡通信高效多了,又有效的規避了網絡層帶來的波動性影響,這是我們用 Java 做後臺沒法做到的事。

切磋,你不能看見別人的功夫套路好,破解難題手到擒來,就輕易決定改練別人的功夫。表面的招式相同,內功可能完全不同,就像金庸小說裏的鳩摩智非要用小無相功催動少林七十二絕技,最後弄的自廢武功的結局。切磋主要是帶給你不同的思維方式,用自己的功夫尋求破解之道。

連結與有效提取

如何選擇乾貨,我在前文《技術乾貨的選擇性問題》中最後給出的結論是,給自己結一張網,形成知識體系。暫時離你的網太遠的技術潮流性的東西,可以暫不考慮,結合功利性和興趣原則去不斷編織和擴大自己的技術之網。在編織了一些新結點入網後,就需要進一步有效提取這些結點的價值。

剛做 IM 時,曾經有個疑惑,就是 IM 的長連接接入系統,到底單機接入多少長連接算合適的?很早時運維對於長連接有個報警指標是單機 1 萬,但當時我用 Java NIO 開 2G 最大堆內存,在可接受的 GC 停頓下,在一臺 4 核物理機上測試極限支撐 10 萬長連接是可用的。那麼平時保守點,使用測試容量的一半 5 萬應該是可以的。

之後一次機會去拜訪了當時阿里旺旺的後端負責人,我們也討論到了這個長連接的數量問題。當時淘寶有 600 萬賣家同時在線,另外大概還有 600 萬買家實時在線。所以同時大概有 1200 萬用戶在線,而當時他們後端的接入服務器有 400 臺,也就是每臺保持 3 萬連接。他說,這不是一個技術限制,而是業務限制。因爲單機故障率高,一旦機器掛了,上面的所有用戶會短暫掉線並重連。若一次性掉線用戶數太多,恢復時間會加長,這會對淘寶的訂單交易成交產生明顯的影響。

他還說了一次事故,整個機房故障,導致單機房 600 萬用戶同時掉線。整個故障和自動切換恢復時間持續了數十分鐘,在此期間淘寶交易額也同比下降了約 40% 左右。因爲這種旺旺在線和交易的高度相關性,所以才限制了單機長連接的數量,而當時已經有百萬級的單機長連接實驗證明是可行的。

在微信春晚紅包的那篇文章裏提到:

在上海跟深圳兩地建立了十八個接入集羣,每個城市有三網的接入,總共部署了 638 臺接入服務器,可以支持同時 14.6 億的在線。

簡單算一下,大概就是 228.8 萬單機長連接的容量規劃,14.6 億怕是以當時全國人口作爲預估上限了。實際當然沒有那麼多,但估計單機百萬長連接左右應該是有的。這是一個相當不錯的數量了,而採用 Java 技術棧要實現這個單機數量,恐怕也需要多進程,不然大堆的 GC 停頓就是一個不可接受和需要單獨調優的工作了。

連結,便是這樣一個針對同一個問題或場景,將你已知的部分連結上新的知識和實踐,形成更大的網,再去探索更多的未知。

...

如何去吸收和消化信息,這是一個智者見智的事情,但在信息爆炸的時代都在忙於過濾和收集信息,卻從不分配點時間去處理和提煉信息,也許你已經忘記了收集的初心了。


最後,是有關微信後臺技術的一些參考文章, 有些在本文中引用了,有些沒引用,但都值得一看。

參考與文後閱讀

[1] 張文瑞. 從0到1:微信後臺系統的演進之路. 2016.01
[2] 張文瑞. 100億次的挑戰:如何實現一個“有把握”的春晚搖一搖系統. 2016.12
[3] 陳明. 微信朋友圈技術之道:三個人的後臺團隊與每日十億的發佈量. 2015.12
[4] 曾欽松. 萬億級調用系統:微信序列號生成器架構設計及演變. 2016.06


轉載自:https://www.cnblogs.com/mindwind/p/6417158.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章