胡通
(中移(杭州)信息技術有限公司 浙江省杭州市 311121)
隨著移動互聯網、5G 的飛速發展,高性能高并發的即時通信,有著非常廣泛的應用場景,例如大家所熟知的微信聊天、大規模消息推送、視頻會議、直播彈幕、智能家居等,總之,一切高實時性要求的通訊場景,都需要高并發的即時通信。
中國移動未來所有接入物聯網的智能家庭寬帶設備,都將是即時通信系統的客戶端,這就意味著推送服務未來會面臨海量的設備和終端接入。為了支持這些千萬級、億級終端,一定是需要強悍的后臺系統,而為了驗證該后臺系統是否能支撐起如此龐大的家庭寬帶設備,需要進行系統的性能測試,也就需要一種方法可以模擬這些百萬、千萬、億級的設備終端軟件測試工具。
目前,即時通信應用大部分是基于WEBSOCKET 協議進行開發,而現有的主流性能測試工具(如LOADRUNNER、JMETER 等軟件),一方面對該協議支持的粒度不夠,無法滿足私有化的交互協議要求,另一方面由于其工具本身采用多線程的模式,模擬百萬連接的資源消耗巨大,甚至無法勝任,因此,測試人員無法高效率地開展WEBSOCKET 協議類的性能測試。
如何針對基于WEBSOCKET 協議的即時通信應用實施性能測試成為了軟件測試研究領域中亟待解決的問題,在這個問題的研究上既存在著機遇又充滿著挑戰。
基于以上研究討論,本文采用互聯網行業中一些高性能的框架和中間件,設計了一種WEBSOCKET 協議的性能測試工具模型,以彌補此類工具的空白。該方法主要借助于NETTY 的異步、事件驅動的特性構建高性能的客戶端性能測試工具,首先,通過自定義協議的編解碼,可以方便的實現移動家寬寬帶設備協議數據的編碼和解碼,可擴展性強;另外,優化改造池化技術,重用連接,防止反復申請和釋放連接,提高連接的使用率和消息的收發能力;然后引入KAFKA 和REDIS 中間件,進行性能指標數據的匯總統計;最后通過測試節點的自動調度,靈活增加客戶端的并發能力,滿足更大規模場景的WEBSOCKET的性能測試需求。
WEBSOCKET 是一個獨立的基于TCP 的協議,是HTML5 新出的一個協議,與HTTP 協議具有一定的交集。在WEBSOCKET出現之前,網站為了實現即時的通信,都是采用輪詢拉取服務端的數據,雖然這種方式可以完成類似于數據推送的通信,但是在即時性和性能上都不如WEBSOCKET 技術。
WEBSOCKET 在建立連接后允許客戶端和服務器之間進行全雙工通信,以便任一方都可以通過建立的連接將數據推送到另一端。WEBSOCKET 只需要建立一次連接,就可以一直保持連接狀態,這相比于輪詢方式的不停建立連接顯然效率要大大提高。
因此,WEBSOCKET 協議具有顯著的2 點特性:

圖1:總體設計圖

圖2:自定義消息
2.1.1 支持推送功能
支持服務器端向客戶端推送功能,服務器可以直接發送數據而不用等待客戶端的請求,實時性強。
2.1.2 減少通信量
只要建立起WEBSOCKET 連接,就一直保持連接,在此期間可以源源不斷地傳送消息,直到關閉請求,相比于HTTP,不但每次連接時的總開銷減少了,而且WEBSOCKET 的首部信息量也更小,通信的效率更高。
由于WEBSOCKET 連接與典型的HTTP 連接請求方式不同,因此其性能測試中關注的性能點也不一樣,結合日常的系統項目工程的應用實踐,重點提煉了3 個性能點。
WEBSOCKET 性能測試的關注點:
(1)系統可以瞬間創建長連接的數目;
(2)系統可以長時間支撐的長連接數目;
(3)系統在保持一定的連接數下推送數據的速度。
與此相應的,可以設計3 種典型的性能測試場景:
場景1:大量連接的創建,即不斷模擬大量用戶對被測系統WEBSOCKET 連接的創建過程。
場景2:長時間穩定保持大量連接,即按照一定速度創建連接,并通過心跳消息保持連接長時間的處于連接狀態。
場景3:大量推送消息,即通過保持一定數目的長連接,然后在各個連接通道上不斷觸發消息推送。
NETTY 是一款異步的事件驅動的網絡應用開源框架,用于快速開發可維護的高性能、高擴展性協議服務器和客戶端。通常該框架應用于服務端的開發,而本文充分借助于該框架的高性能的能力設計了一個典型客戶端工具,應用于WEBSOCKET 協議的性能測試。
該性能測試工具設計模型核心主要包括測試分布式調度、壓測節點、數據采集3 個模塊,如圖1所示。首先,測試分布式調度會根據測試任務規模級別信息自動選擇可用的節點機器發起分布式的測試任務,然后壓測節點借助于NETTY 的自定義編解碼方式,完成私有協議的數據解析;之后通過優化配置池化和共享技術提升客戶端的建立連接的能力;最后數據采集通過REDIS 和KAFKA 中間件存儲統計器計算出連接數、發包速度、收包速度、丟包率等性能指標。
若要模擬百萬千萬的大規模的性能測試要求,可以根據壓測節點機器的配置或資源結合一定的算法自動分配不同壓測節點的壓測任務大小,然后再采用分布式的方式發起壓力。通過該方式,可以最大限度的發揮不同壓測節點的資源,提高壓力產生的能力,滿足大規模需求。
3.3.1 壓力產生器
壓力產生器采用SPRINGBOOT 技術作為服務框架,基于高性能網絡框架NETTY 進行構建,與NETTY 提供的線程模型進行緊密結合。
基于NETTY 線程模型改造執行線程池,任務執行線程池維護一個多路復用的線程池,每個線程對應多個連接,對應NETTY框架中每個線程對應多個CHANNEL,另外任務執行線程池中還包括一個連接池,該連接池為基于線程池創建的CHANNEL 連接池,在初始化階段,為每個線程創建多個CHANNEL,并且這些CHANNEL 是可以復用的,當一個實例被提交給一個線程時,該線程為該實例分配一個可用的連接供使用。每個實例執行時是非阻塞的,所以即使是單線程也可以達到較高的并發量。
通過優化改造NIO 異步多線程模型,設定同一個EVENTLOOP會被多個CHANNEL 所共享,這使得可以通過盡可能少量的THREAD 來支撐大量的CHANNEL,而不是每個CHANNEL 分配一個THREAD,并結合多路復用器SECLTOR,使得少量資源即可模擬成千上萬的移動家寬寬設備客戶端。
3.3.2 自定義消息
移動家庭寬帶設備采用自定義的WEBSOCKET 的協議進行設備的登陸和數據的上報,如圖2所示,采用5 個字節的消息頭和不定長的消息體,其中消息頭包括開始標志、消息類型和消息長度,消息長度代表消息體的大小。
本文采用NETTY 的自定義編解碼器,可以方便靈活的完成協議數據的交互,根據協議規定要求添加一個CHANNELHANDLER的編碼器和解碼器即可進行消息的處理。
主要是通過NETTY 提供的LengthFieldPrepender 編碼器,自動計算當前待發送數據的長度,并將該長度添加到當前數據的頭部形成待發送的完整數據楨。在接收數據的時候,使用的是LengthFieldBasedFrameDecoder 解碼器,通過該解碼器首先根據開始標志和5 字節長度自動截取數據頭,并解析出消息類型和消息長度,然后依據該消息長度在此后截取消息體部分的字節流,解決數據傳輸過程中出現粘包問題,完成數據解碼,保證數據的正確性和安全性,最后再交給專門的事務處理器HANDLER 進行處理和響應。
WEBSOCKET 長連接的性能指標數據不同于常見的HTTP 請求,性能關注點側重于連接數、收包速度、發包速度、丟包率等。另外,傳統的同步服務調用,發起服務調用之后,業務線程阻塞,等待響應,接收響應之后,業務線程繼續執行,對發送的消息進行累加,獲取性能指標數據;而使用NETTY,所有的網絡I/O 操作都是異步執行的,不能和同步執行一樣計算性能指標。
本文借助于REDIS 的原子性操作,匯總統計壓測節點建立的WEBSOCKET 連接個數,避免臟寫。另外通過使用KAFKA 作為消息隊列來收集WEBSOCKET 長連接收發的性能消息數據,可以降低系統之間的耦合度。KAFKA 消息隊列方式可以控制消息消費者消費的速度,可以提高消息處理效率并避免由于消費速度過慢導致消息堆積或消費速度過快導致長時間等待的情況。
另外,如果壓測節點存在多個共同參與施壓,則可以建立多個KAFKA 消息隊列,依次存放每個執行節點單獨上報和接收的數據,之后再進行匯聚計算。
通過這個方式可以便捷地獲取壓測過程中的長連接性能指標,觀察被測系統的穩定性和狀態。
本文主要設計思路借鑒了很多開源框架的高性能設計原則,始終本著使用簡單,低耦合和高內聚的軟件設計思路進行實現,采用的都是通用插件式設計,方便后續擴展。
該性能測試工具設計模型通過優化的池化和重用線程方式來消除由上下文切換所帶來的開銷,能夠適應快速增長的移動設備的規模,可以在保持服務資源低成本的同時,模擬更大量的家庭寬帶設備,伸縮能力強。
并且,利用NETTY 自定義編解碼器,能夠高效地完成移動家庭寬帶設備的WEBSOCKET 協議的自定義數據交互,靈活性高。
最后,結合高性能中間件REDIS 和KAFKA,采集、統計分析出WEBSOCKET 長連接的連接數、發包速度、收包速度等特殊的性能指標,彌補了WEBSOCKET 長連接協議無法有效開展性能測試的技術空白,豐富了性能測試手段和方式。