線上服務CPU飆升
前言
- 功能開發(fā)完成僅僅是項目周期中的第一步,一個完美的項目是在運行期體現(xiàn)的
- 今天我們就來看看筆者之前遇到的一個問題CPU飆升的問題卸夕。 代碼層面從功能上看沒有任何問題但是投入使用后卻讓我頭大
問題描述
- 系統(tǒng)上點擊數據錄入功能在全局監(jiān)控中會受到相關消息的通知。此時服務器CPU飆升300%
問題定位
- 首先我們先梳理下
Websocket
的數據發(fā)送的簡單原理示意圖。往往定位問題得清楚我們的邏輯是什么 - 當一個客戶端啟動時除了和
Websocket
建立連接之外,我們還需要向Websocket
服務注冊當前客戶端需要哪些接口的實時數據 - 我在代碼內部是通過一個Map來存儲這些接口簽名信息的烧颖。然后客戶注冊時候將這些接口和客戶端綁定在一起
- 當我們監(jiān)聽程序堅挺到數據變動就會對綁定到相關接口的客戶端發(fā)送最新數據
image-20210510152226364
業(yè)務定位
- 業(yè)務上很好定位,問題就是出現(xiàn)在我們的監(jiān)聽程序中窄陡。當監(jiān)聽到數據給
websocket
客戶端發(fā)送訂閱的最新變動接口時就會出現(xiàn)CPU飆升炕淮。持續(xù)時間還很長,稍等一會就會降下來 - 這很明顯是我們推送消息的時候出現(xiàn)了問題
image-20210510161532550
隔離業(yè)務看本質
- 作為一個合格的程序員呢跳夭,必須擺脫業(yè)務才能有所收獲 涂圆。業(yè)務是我們代碼的外殼所有的問題基本上都是我們本質的問題。我們線上使用用戶1W內币叹。在這種的并發(fā)場景下應該是不會出問題的∪笄福現(xiàn)在出了問題肯定我們的程序邏輯有缺陷
image-20210510162405407
- 上面是我們的發(fā)送消息的代碼。代碼也很簡單颈抚。先獲取所有符合發(fā)送條件的客戶端 踩衩。然后通過客戶端內部提供的
sendMessage
方法進行推送。 - 但是這個時候的
message
是我們的接口信息贩汉。在內部會基于客戶端保存的方法簽名進行反射調用從而獲取最新數據驱富。在推送給客戶端的 - 在上面的代碼中核心的是
WebsocketManager.messageParse
。這段是獲取消息然后發(fā)送雾鬼。里面獲取消息是基于resultful格式解析的
image-20210510163121570
- 這個方法內部我們有內置了我們的四種解析方式萌朱。這里我們只需要關心
RequestMappingMessageParseHandlerImpl
這個協(xié)議。
image-20210510163300402
- 關于我們內部的協(xié)議這里也不需要太在意策菜。這是我們自己的一個設計。根據上面的圖示我們也能看的出來里面
RequestMappingMessageParseHandlerImpl
是核心
image-20210510163412669
產生原因
- 上面我們簡單的梳理了下代碼的邏輯酒贬。
- 仔細分析下我們是遍歷所有客戶端然后在反射調用接口數據進行返回的又憨。實際上在消息推送時我們沒必要在每個客戶端內部調用數據。我們完全可以先調用數據然后在遍歷客戶端進行發(fā)送锭吨。
- 這也是導致CPU過高的問題蠢莺。我們1W個用戶同事在線的可能有5000+ 。 那么我們需要5000次以上的反射著肯定是吃不消的零如。這也是為什么本文開頭說功能正常不代表業(yè)務正常躏将。
解決方案
- 這就是量變引起質變。在多客戶的情況下我們的設計弊端就暴露出來考蕾。這里也是筆者自己給自己挖坑祸憋。既然找到問題我們就好解決了。下面我們對代碼做了一下改動
image-20210510164137467
我將數據緩存起來肖卧。因為在同一批次推送時本來也應該保證數據一致性蚯窥。而且我們系統(tǒng)對數據實時性也是可以接受一定時間延遲的。我在這里又加上緩存這樣就解決了我們循環(huán)的問題
經過測試本次改動在CPU上大概優(yōu)化了100倍。
總結
- 功能開發(fā)完成僅僅代表功能的實驗沒有問題
- 單用戶和多用戶完全是兩種不同的用戶形態(tài)拦赠。我們功能設計初期就應該盡量考慮數據量的問題
- 唯一做的好的地方是我通過責任鏈模式將數據解析隔離出來巍沙。否則這樣的問題定位將會更加麻煩