背景介紹
在服務剛啟動的時候,服務的運行狀態并沒有達到最佳,如果一下子將流量提升到日常運行的狀態,會存在大量的請求超時。
為什么服務剛啟動的時候,服務不是最佳狀態呢?
- JAVA應用類加載是按需加載的,在服務剛啟動的時候,只會加載啟動過程中需要的類;當服務接口被調用的時候,才會加載、初始化接口用到的類;對于熱點代碼,存在一個字節碼解釋執行到本地機器碼執行的過程。
- Java應用需要與依賴的數據庫創建連接,與數據庫的連接不僅僅是建立一個TCP連接,還涉及用戶認證、權限校驗、數據庫資源分配等,是一個比較耗時的操作。
- Java應用與依賴的redis、HSF服務都要做初始化工作… …
為了服務達到最佳狀態,我們通過調整服務權重慢慢增加流量,經過一段時間的小流量預熱,讓系統達到最佳運行狀態。
某天我決定干這個事,我將權重由1調整到2,系統正常;由2調整到4,系統正常;將權重調整到9的時候,故障出現了。
故障描述
將權重調整到9,網關將流量都打到了30多臺機器上(一共168臺機器),接著應用不斷發生CMS GC,接口成功率直線下降。
下游沒流量的機器
下游沒流量的機器
下游機器接口成功率
下游機器接口成功率
分析過程
因為提前完全沒有想到發生這種情況,一點預案也沒有。后來冷靜下來,想明白:這不應該是應用側的問題,調整個權重就被打掛似乎沒有這種道理。
系統交互關系
系統交互圖
第4步-List<Host>
機器不健康的時候,注冊中心會剔除掉問題機器;機器恢復健康后,注冊中心會再次將機器加入到列表中,【機器列表的順序不會改變】。
舉例說明:
- A1、A2、A3、A4(權重都是1)已經注冊到注冊中心,狀態健康;
- A1發送心跳失敗(網絡超時/服務hang住等原因),client從注冊中心獲取的機器列表是:A2、A3、A4;
- A1發送心跳成功(恢復健康狀態),client從注冊中心獲取的機器列表是:A1、A2、A3、A4。
第5步-構造下游機器列表
網關從注冊中心拿到List<Host>后,構造下游機器列表的邏輯:
構造下游機器列表
舉例說明:
List<Host>中共有A1、A2、A3、A4四臺機器,每臺機器權重是3,該方法構造出的下游機器列表是:
[A1,A1,A1,A2,A2,A2,A3,A3,A3,A4,A4,A4]
第6步-選擇一臺下游機器
當網關收到第一個請求的時候,選擇下游機器列表的第一個機器;
當網關收到第二個請求的時候,選擇下游機器列表的第二個機器;
依次輪詢下游機器列表。
輪詢邏輯
小結
每當某臺機器向注冊中心發送心跳超時的時候,該接口在注冊中心對于的機器列表就會變化;
網關會獲取該接口新的機器列表List<Host>,并根據List<Host>重新構造一個新的下游機器列表;
新的請求會按照下游機器列表的順序輪詢發送到后端業務機器上。
故障場景復盤
基本信息
業務應用有168臺機器,定義為:A1,A2,A3… …A168;
網關有大概600臺機器。
場景復盤
- 業務應用將權重調整為【9】;
- 網關機器列表(所有網關機器上都是這個列表)變更為:[A1,A1,A1…A1,A2,…A2,…A168,A168,A168…A168],列表中有9個A1,9個A2,9個A3,…,9個A168,計數設置為【0】;
- 單臺網關機器QPM:300,【2】中數組每臺機器對應9個元素,由于網關是輪詢策略,所以一分鐘的流量【整個集群的】打到了【300/9=33】臺機器上;
- 由于流量打到了30多臺機器上,機器負載迅速增大->發生了CMS GC,系統hang住,進而導致業務應用向注冊中心發送心跳失敗,注冊中心通知網關機器列表變更,流程回到了第【2】步。