本報告主要分為兩部分,性能測試和消息可靠性測試。前者主要關注吞吐,延時,同時線上使用者等,即通常所說的性能名額。後者主要模拟真實環境(比如離線,線上,弱網)消息通道的可靠性。
先說結論,對于容量和性能:
性能及容量總結
伺服器資源: 8核16G記憶體, 6個機械磁盤,每個磁盤100G, 用于mongo分片,10MB帶寬。
容量:使用者容量10萬以上,消息條數10億條。
性能評估:同時線上使用者10萬,每秒鐘發送消息900條,消息延時1秒(從發送者發出消息到接收到消息)
可靠性總結
啟動sdk,模拟50個使用者線上、離線情況,消息可靠性100%。
發送10萬消息,有3條失敗,其他消息都能被對方精确收到,并成功落地本地db。對于失敗的3條消息,接收方确實沒有收到,系統消息是一緻的。
項目介紹
OpenIM是由前微信技術專家打造的開源的即時通訊元件。Open-IM包括IM服務端和用戶端SDK,是一套整體的解決方案,代碼開源,一切可控,
OpenIM可以實作全平台支援,目前支援Android,iOS,Flutter,Uni-app,react-native, JSSDK等。
OpenIM可以應用在企業内部辦公,dating交友,線上客服等項目,也可以用于元宇宙。
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帶寬。
子產品 | 性能情況 | 說明 |
msg_gateway | 部署多個 | ,同時線上5萬*2=10萬 |
mongodb | 6分片,每個磁盤對應一個分片 | 1800條/每秒消息入庫 |
約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 |
發送資料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萬。