坑爹,線上同步近 3w 個用户導致鏈路阻塞引入發的線上問題,你經歷過嗎?

語言: CN / TW / HK

theme: Chinese-red

本文正在參加 「金石計劃 . 瓜分6萬現金大獎」

分享一個印象深刻的線上問題,希望能夠給 xdm 帶來一點思考

一個稀鬆平常的工作日,正準備下班的時候,不巧,突發線上緊急問題,心中一萬個不情願,可還是要硬着頭皮去定位問題

簡單的表象為微服務之間 gRPC通信的通道默認是用了默認值,並沒有按照實際業務去設置通道接受和發送的字節大小

用過 golang grpc 通信的 xdm 就知道,grpc 通道默認的發送和接收的消息大小為 4M,由於傳送的數據包大於了 4M,導致通道阻塞,一直報錯 rpc 錯誤,

rpc error: code = ResourceExhausted desc = grpc: received message larger than max (6394324 vs. 4194304)

於是便有了一個定位並想辦法解決或者規避問題的慢慢長夜

簡述基本介紹通信流程

整個業務架構比較複雜,我們簡單的提出出問題的服務鏈路來進行闡述

服務 A - 專門和第三方對接

有一個服務 A 是專門找第三方同步源讀取第三方系統的用户組織結構,並轉換成咱們平台自己的數據結構,將數據發送給 服務 B

服務 B - 專門處理用户相關數據

服務 B 專門處理關於用户組織結構數據的,處理完畢之後落盤,並將數據給到服務 C

服務 C - 主要做數據轉發

服務 C 主要是做數據的通道,會將數據轉發給到節點中的應用 D,因為 應用 D 和 服務 A 和 服務 B 沒有辦法直接通信

應用 D,處理處理實際的流量即管控

應用 D 接收或者去找 服務 C 拉取數據後做相應的業務,做基本的流量管控和用户認證等

其中上述兩者之間都是通過 gRPC 的方式通信

問題 1 - rpc 通道發送和接收消息設置過小

萬萬沒想到的是,在做第三方組織結構同步的時候,居然是服務 A 從第三方同步源中獲取所有的用户組織結構(包括所有的組,所有的用户),不管總量多少,一口氣全部弄過來,然後再一口氣全部推給 服務 B

可這一次線上問題,正是因為這麼 low 的做法和處理方式,導致超出了 gRPC 的默認消息大小 4M,顯現就是服務 A 將數據發送給到服務 B 的時候,發送沒有問題,但是服務 B 接收的時候出了問題,日誌中瘋狂打印上述的 rpc 錯誤

rpc error: code = ResourceExhausted desc = grpc: received message larger than max (6394324 vs. 4194304)

當然,這個時候不允許我們停下來去思考如何優化的事項,必須第一時間解決或者規避問題

立刻評估,將涉及到的服務,gRPC 的 send 和 receive 的地方全部統一修改為 32 M(1024102432) ,這個問題暫時得以規避

可以查看到 grpc 源碼中的説明

MaxRecvMsgSize 接受消息 在 grpc 中默認大小為 $$$$4M --(102410244)= 2^{22} $$$$

MaxSendMsgSize 發送消息在 grpc 中默認是$$$$ MaxInt32 = 1<<31 - 1 也就是 4M * 2^9 -1 即 2048 M = 2G $$$$

則在 grpc.NewServer 的時候,將上述的 option 加上去就可以了,例如這樣

// 例如設置接收消息大小為 math.MaxInt32 var opts []grpc.ServerOption opts = append(opts, grpc.MaxRecvMsgSize(math.MaxInt32)) newSvr := grpc.NewServer(opts...) // grpc RegisterxxxxServer()

問題 2 - 組織結構同步的層級太小,不支持 16 層

本以為問題就這麼規避了,然而還是太年輕

當服務 A 將數據全部打包發送到服務 B 的時候,才發現,原來問題才剛剛開始,由於數據量比之前測試過的數據量大了好幾倍,導致各種問題接二連三的出來,這也體現了整個平台的健壯性太差

當前遇到的問題是服務 B 處理的組織結構層數,最大隻能有 8 層,超過 8 層的數據就直接不要了😂😂😂,看到這裏,what??? 之前是誰設計的😢😢😢

迅速閲讀源碼,查看相關聯的邏輯,調整關聯代碼,火速將支持的 8 層,調整為 16 層組織結構,還好代碼不是太複雜,否則這麼大半夜的,真的不敢大動,畢竟這個時候已經不是一個人了,只是半個人😅😅😅

此處的組織結構好在不是傳遞的一棵樹,如果是一棵樹的話,大概率是要棧空間超限的,我們知道棧空間一般 2M,超過就要溢出了

好在是傳遞的是一個用户的 list,元素是關於用户的絕對路徑,例如 /a/b/c/小花

問題 3 - 同步近 3 w 個用户居然花了近 8 分鐘,近 3w 用户放到一個消息裏

處理完問題 2 之後,嘗試同步一次組織結構,發現近 3 w 的用户,居然同步花費了 8 分鐘左右,其中 服務A 到服務B耗時近 2 分鐘,然後服務 B 就處理了4 分鐘,其餘時間花費在別的服務處理

這個時候,就看到頁面上一直在轉圈,如果是用户看到一幕,那可能直接就是退貨的節奏了🤣🤣

不過好在是同步成功了,暫時先把同步的按鈕先關了吧,畢竟數據都過來了,接下來的事情都是可以調整的

其實看到這裏,一個消息放近 3 w 個用户,只能説這一塊根本沒有設計,需求是趕鴨子上架趕出來的吧?對於這一塊的優化放到下一篇來分享

問題 4 - 操作界面一直轉圈,前端處理數據極慢

然而,當上一個問題還沒有完全解決的時候,發現又爆出另外一個問題,看來年輕的不僅僅是一點點大,這個時候甲方爸爸要開始喊 細狗你行不行啊

打開平台,查看用户相關頁面,卡的一匹,足以和上世紀打開網頁的程度比慢了,簡直沒眼看,。處理 3 w 數據耗時 20 分鐘,才看到正常的頁面

原來處理方式是這樣的:

前端找後台查詢這個租户所有的用户,然後前端再進行樹形展示,看到這裏是不是蒙圈了???

哪有這麼去實現功能的?基本的懶加載不會嗎?暫時後端提供相應的接口, 前端 調整邏輯得以規避

懶加載, 是一種獨特而又強大的數據獲取方法,它能夠在用户滾動頁面的時候自動獲取更多的數據,而新得到的數據不會影響原有數據的顯示,同時最大程度上減少服務器端的資源耗用

然後,大部人的回答是,我也不知道會有那麼多數據呀🤣🤣🤣,誒,還是吃虧在太年輕,不夠專業,甚至還有人提議,這個頁面暫時讓客户不要點😥😥

問題 5 - 大數據日誌上報,由於數據量猛增,導致日誌通道阻塞

真是福無雙至,禍不單行啊

由於大數據日誌上報模塊也需要通過 grpc 根用户數據更新時間來一次性查詢用户,同樣的問題,這一條鏈路也卡的要死,顯示由於請求超時,因為 rpc 超時時間代碼中默認就設置了 10 s , 後面將時間改大了之後得以規避,最終由於大數據模塊處理數據慢,花費了 2 個小時才把近 3w 的數據搞定

心中想,這也是大數據??😪😪,感覺這個產品要完蛋了

問題 6 - 系統本身僅支持 1 w 用户,前線默默的攬了一個 5w 用户的客户

處理到這裏,天也漸漸矇矇亮了,從性能測試的小夥伴報告中瞭解到,之前做的性能測試最多就支持 1 個租户下有 1w 個用户,然而,銷售吹牛皮招攬了遠遠大於這個數的客户,且還不告知研發內部

其實銷售也沒想到,怎麼我們的平台這麼弱雞?逐步喪失信心。。

實際上出現的問題遠遠不止上述幾個,接下來便是無盡的優化和思考,希望暴露的問題能給 xdm 帶來一些提醒和思考

無論之前架構如何,對於第三方組織結構同步的時候,咱們需要考慮這些問題

  • 產品的基本數據指標要同步給前線等相關方,避免自己人坑自己人,火急火燎的,很難有效的解決好問題
  • 從第三方獲取組織結構的時候,基本的分頁要有,不僅僅是分頁從第三方同步源獲取,還要分頁的給出去
  • 對於服務 A 將數據給到服務 B 的時候,先分頁給組,再分頁給用户
  • 對於 rpc 中消息大小設計,需要在設計之初根據業務考慮到可能的消息情況,去設置一個合理的數值
  • 對於同步數據的時候,需要分步驟,分狀態來進行處理,需要實現斷點續傳,需要能夠應對各種異常場景
  • 對於前端從後台獲取用户樹的時候,務必記得使用懶加載(簡單來説就是可以先獲取最外層的組,當點擊到某個組的時候,再去查詢這個組下面的用户信息和子組信息,而不是一口氣將整個租户的組織結構加載出來🤣🤣,這也太心大了)

自然,實際落地的時候還需要考慮很多,我們需要更多的往前看一步,不給別人留坑,也不給自己挖坑

自然我們需要提升自己的能力,提升自己的思維,不斷向大佬看齊,可能低級問題我們都會犯,但要有進步,要有變化,做需求,做設計,需要考慮更加全面,否則你永遠不知道你帶來的影響有多大

關於如何去優化第三方組織結構同步這個功能,感興趣的朋友可以思考一下,評論區討論一下哦,下一篇會進行闡述對其優化的方法,以及落地的方案

文中提到的技術感興趣的可以查看如下相關文章,或者自行擴展:

  • 懶加載
  • 分頁,同步

感謝閲讀,歡迎交流,點個贊,關注一波 再走吧

歡迎點贊,關注,收藏

朋友們,你的支持和鼓勵,是我堅持分享,提高質量的動力

好了,本次就到這裏

技術是開放的,我們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。

我是阿兵雲原生,歡迎點贊關注收藏,下次見~