# 再次推薦github 6.7k star開源IM專案OpenIM效能測試及訊息可靠性測試報告
本報告主要分為兩部分,效能測試和訊息可靠性測試。前者主要關注吞吐,延時,同時線上使用者等,即通常所說的效能指標。後者主要模擬真實環境(比如離線,線上,弱網)訊息通道的可靠性。
先說結論,對於容量和效能:
效能及容量總結
伺服器資源: 8核16G記憶體, 6個機械磁碟,每個磁碟100G, 用於mongo分片,10MB頻寬。
容量:使用者容量10萬以上,訊息條數10億條。
效能評估:同時線上使用者10萬,每秒鐘傳送訊息900條,訊息延時1秒(從傳送者發出訊息到接收到訊息)
可靠性總結
啟動sdk,模擬50個使用者線上、離線情況,訊息可靠性100%。
傳送10萬訊息,有3條失敗,其他訊息都能被對方精確收到,併成功落地本地db。對於失敗的3條訊息,接收方確實沒有收到,系統訊息是一致的。
專案介紹
OpenIM是由前微信技術專家打造的開源的即時通訊元件。Open-IM包括IM服務端和客戶端SDK,是一套整體的解決方案,程式碼開源,一切可控,
github地址:https://github.com/OpenIMSDK/Open-IM-Server
開發者中心: https://doc.rentsoft.cn/#/
在單機的情況下,模擬線上使用者發訊息流程,線上使用者量和訊息量達到一定量級後,系統CPU、記憶體、磁碟佔用、以及訊息時延情況。以確定使用者群體達到一定量級後,對伺服器資源的預先評估。本次測試並不極限測試,一是因為生產環境本來都會有使用者量和訊息量的限制,二是因為OpenIM的訊息模型,訊息傳送首先都會通過websocket入庫kafka,理論上傳送訊息的寫入效能是兩者的組合,而訊息傳送的真正瓶頸實際在mongodb的隨機讀寫。
測試過程
伺服器資源: 騰訊雲主機(香港)1臺:linux Ubuntu 18.04.4系統,4核8G記憶體,單塊機械硬碟。5Mb頻寬。
測試條件:去掉訊息入庫mysql(因mysql僅用於管理後臺,不影響線上使用者服務)。日誌級別調整為4或更低。kafka設定2個分割槽,msg_transfer 2個。
測試流程:1個客戶端(成都,window pc,4核16G記憶體)啟動1萬個協程,模擬使用者與伺服器建立websocket長連線,間隔時間為隨機50-100秒之間。兩個客戶端共模擬2萬用戶同時線上,傳送訊息,觀察訊息流轉各個模組的處理能力,共計2500萬條訊息,觀察系統記憶體、磁碟資源使用情況。
測試結論和分析
| 關注指標 | 測試結果 |
| ---------------- | ------------------------------------------------------------ |
| 同時線上人數 | 20000個 |
| 閘道器接收訊息速度 | 150條/s(因為瓶頸不在此,故意控制傳送速度,以確保kafka能被快速消費入mongodb) |
| mongodb處理寫入 | 300條/s (收件箱模型,導致訊息一拆為二) |
| CPU使用率 | 約50% |
| 記憶體使用率 | 約4G(mongo記憶體限制2G,由於每個文件儲存5k條訊息,實際實際索引量很小。 redis只存了使用者seq對映關係,基本不佔記憶體) |
| 傳送訊息響應時長 | 平均70毫秒 |
| 傳送過程時延 | 平約1秒 |
| 磁碟空間 | mongo中5000萬條訊息佔用10G磁碟,由於一拆為二的緣故,mongo的50000萬條訊息,實際為2500萬條訊息。
|
mongodb資料情況
redis資料情況
磁碟狀態
資源佔用分析
(1)redis記憶體消耗極小,一個使用者一條資料(包括token和seq),和使用者量成正比,3萬用戶佔用幾十M記憶體。
(2)mongodb如果去掉cache,記憶體消耗極小,每個document存放5000條訊息,與使用者量和訊息量成正比,3萬用戶,2500萬訊息,索引才950K(更好的方式檢視mongo消耗cache之外的記憶體)
(3)2500萬訊息,磁碟空間佔用10G。
(4)每秒鐘150條訊息,cpu整體佔用50%,即2核。
效能分析
(1)效能瓶頸在mongodb寫入操作,1條訊息,需要按照發送者和接收者拆分2次,mongodb寫入2次,未來可以針對mongodb讀寫進一步優化。
(2)對於cpu消耗較大的模組,未來做一次整體優化。
(3)效能很平穩,不會隨著資料量增加而降低。機械磁碟iops 達到200基本達到了裝置的極限
單機效能預估
伺服器資源: 8核16G記憶體, 6個磁碟,每個磁碟100G, 用於mongo分片,10MB頻寬。
效能評估:同時線上使用者10萬,每秒鐘傳送訊息900條,訊息延時1秒(從傳送者發出訊息到接收到訊息)
| 模組 | 效能情況 | 說明 |
| -------------------- | ---------------------------------------- | -------------------------------------------------------- |
| msg_gateway | 部署多個 | ,同時線上5萬*2=10萬 |
| mongodb | 6分片,每個磁碟對應一個分片 | 1800條/每秒訊息入庫 |
| CPU使用率 | 約100% | 需要優化模組 減少cpu消耗 |
| 記憶體使用率 | 小於8G(mongo記憶體限制2G) | 如果記憶體富餘可以增加mongodb的cache大小 |
| 傳送訊息響應時長 | 平均70ms | |
| 傳送過程時延 | 平約1s | 從傳送者到接收者 |
| 10億條訊息,磁碟空間 | 佔用40*100G磁碟,每個磁碟大概佔用70G空間 | 對於群聊屬於擴散寫,磁碟消耗較大。整體要考慮磁碟空間富餘 |
未來工作優化
(1)mongo叢集部署,支援上億使用者同時線上,千億級訊息;
(2)簡化叢集部署;
(3)資料備份、恢復工具;
以上主要對服務端效能做了一個大致測試,但一套完整的IM解決方案,不僅僅是服務端的工作。實際上,客戶端重要性毋庸置疑,具體包括如何利用seq和服務端同步訊息,如果保證訊息收發的時序,如何回撥客戶端(會話改變、新增,新訊息),訊息落地本地db,seq同步,訊息推拉如何結合以確保訊息收發可靠性。
訊息可達率(可靠性)測試
相比於效能測試,實際上,訊息的可達性(可靠性)更為重要。所以,我們在做效能測試的同時,也要對訊息的可達性(可靠性)進行測試,如果不能保證訊息收發的正確性,再高的效能也是徒勞。本文重點總結關於OpenIM對於訊息可達性測試的方案、過程以及結果。先說結論,OpenIM訊息可達率100%,大家可以放心使用在生產環境中。seq對齊和同步機制,保證了OpenIM的訊息可達性是業界領先的。
訊息可達性(可靠性)的定義
IM訊息系統的可靠性,通常就是指訊息投遞的可靠性,即我們經常聽到的“訊息必達”,通常用訊息的不丟失和不重複兩個技術指標來表示。確保訊息被髮送後,能被接收者收到。由於網路環境的複雜性,以及使用者線上的不確定性,訊息的可靠性(不丟失、不重複)無疑是IM系統的核心指標,也是IM系統實現中的難點之一。總體來說,IM系統的訊息“可靠性”,通常就是指聊天訊息投遞的可靠性(準確的說,這個“訊息”是廣義的,因為還存使用者看不見的各種指令和通知,包括但不限於進群退群通知、好友新增通知等,為了方便描述,統稱“訊息”)。
從訊息傳送者和接收者使用者行為來講,訊息“可靠性”應該分為以下幾種情況:
(1)傳送失敗,對於這種情況IM系統必須要感知到,明確反饋傳送方。如果此訊息沒有傳送成功,傳送方可以選擇重試或者稍後再試。
(2)傳送成功,如果接收方處在“線上”狀態,應該立即收到此訊息。如果接收方處在“離線”狀態不能收到訊息,一旦上線則立刻收到訊息。
(3)訊息不能重複,用數學術語表示:“有且僅有這條訊息”,如果重複了,可能表達的意思就變了。 總之,一個商用 IM系統,必須包含訊息“可靠性”邏輯,才能談基本可用,這是IM系統最基本也是最核心的邏輯。
模擬場景&測試方案
網際網路真實場景複雜,但客戶端大體可以分為兩種情況:(1)傳送訊息時,接收方線上,能收到訊息;(2)傳送訊息時接收方不線上,登入後能收到離線訊息。我們用測試程式模擬網際網路客戶端各種場景,按照登入、傳送訊息、接收訊息的情況,把測試客戶端分為以下2種類型:
(1)啟動測試時離線,隨機sleep 0-60 秒後登入,傳送訊息,且接收訊息
(2)啟動測試時離線,隨機sleep 0-60 秒後登入,不傳送訊息,只接收訊息
test.ReliabilityTest(oneClientSendMsgNum, intervalSleepMs, imIP, randSleepMaxSecond, testClientNum)
在實際測試中共計50個客戶端,約25個(50%概率)客戶端不傳送只接收訊息,約25個(50%概率)客戶端傳送且接收訊息 。
傳送模式:每個客戶端隨機選擇其他客戶端作為訊息接收者;
測試預期: 每一條傳送成功的MsgID,都能在接收的訊息列表中找到,同樣,每一條接收到的MsgID,都能在傳送成功的訊息列表中找到。
具體做法:(1)訊息傳送成功後,通過OnSuccess回撥,記錄MsgID; 收到新訊息後回撥OnRecvNewMessage,記錄MsgID;(2)週期性對比兩個訊息列表,確認是否完全一致;
測試結果
| 傳送訊息客戶端 | 接收訊息客戶端 | 預設傳送訊息總量 | 傳送成功條數 | 傳送失敗條數 | 接收訊息條數 | |
| -------------- | -------------- | ---------------- | ------------ | ------------ | ------------ | ---- |
| 25個 | 50個 | 100000條 | 99997 | 3 | 99997 | |
傳送資料100000條,其中失敗3條,9999997條成功,接收方成功接收9999997條訊息(接收方成功接收到訊息,寫入本地db,並能觸發訊息回撥)
每一條傳送成功的訊息,對方都能準確接收到,無論接收方在訊息傳送時的登入狀態是線上還是離線。
每一條傳送失敗的訊息,對方都不會收到。
測試程式
main/main.go
intervalSleepMs := 1
randSleepMaxSecond := 30
imIP := "127.0.0.1" //OpenIM ip
oneClientSendMsgNum := 4000 //每個客戶端傳送的訊息條數
testClientNum := 50 //同時啟動壓測客戶端數量
func main() {
reliabilityTest()
}
注意事項:
(1)控制壓力,因為sdk需要寫本地db,客戶端會成為壓力瓶頸。
(2)壓測客戶端日誌會影響測試效能。
成本對比
此表格是某IM雲平臺的價格,如果按照10萬月活,儲存三年訊息來算,大概每年需要支付15萬。而採用OpenIM只需要採購雲主機,每年成本約0.8萬。