分享 | 張皓聰(愷英網絡程序經理)php
整理 | 西北前端
2016年10月29日,由又拍雲舉辦的Open Talk No.26在「魔都」上海3W空間成功舉辦,這次活動主要邀請直播領域開發一線的技術大神們聊一聊直播平臺的架構與優化,看他們化解項目選型、開發上線、迭代過程、性能優化中遇到的挑戰與經驗。node
愷英網絡程序經理張皓聰在Open Talk No.26上,作了「直播平臺IM系統實戰」的主題分享,介紹了直播平臺「IM系統」的搭建過程。redis
張皓聰:2010年加入愷英網絡,前後負責過多款手遊頁遊項目,對NodeJS和ZeroMQ有深刻研究。目前負責愷英網絡「板栗直播」IM系統的相關開發工做。後端
△ Open Talk No.26現場照片性能優化
如下是張皓聰的分享全文服務器
今天我要跟你們分享的是如今很是流行的直播平臺裏的「IM系統」。「板栗直播」IM系統從立項初期,到發展致今,已經成爲一個功能複雜,擁有必定穩定性的系統。網絡
咱們作IM系統1.0版本時,有如下幾個需求:架構
一週出 Demo併發
差很少能用
演示的時候不能忽然崩潰
萬一崩潰了不能被發現
性能無所謂
總之,要快速作出來
因爲只有一週的時間,初版的架構搭建得很是簡單:
服務端由 PHP 和 NodeJS 組成。PHP 負責用戶帳戶,註冊和登陸。NodeJS 負責長連以及進出房間和推送,作成簡單的交互。
第一個版本的前端服務器的結構很是簡單,功能不多,僅僅負責聯接客戶端,負責全部客戶端的聯接。爲了保證 H5 也可使用,所以在長連上選擇了WebSocket。
△ IM系統1.0版本架構圖
爲了確保客戶端演示的時候,萬一崩潰了也不能被察覺,咱們使用 pm2 守護了 node進程。同時讓客戶端同窗作了個自動斷線重連。
而後登陸流程是這樣的,先從PHP獲取一個token,而後帶着這個token和node 創建鏈接,node 會在內網向php驗證 token 是否有效,而後創建鏈接。
而後廣播方面,使用了我稱做組的形式進行廣播,全部的房間和私信都是經過組的形式進行廣播。
首先,用戶在房間裏發言的時候,其實是往特定組發送廣播。組的名字是用特定字符開頭來歸類的。
舉例來講,須要向10000房間發送聊天信息時,其實是向 r:10000 這個組發送信息。此時服務端會遍歷該組裏全部鏈接,併發送消息。
當須要進行全服廣播的時候,咱們使用一個寫死特殊的 0 號房間,轉換成 gid 也就是 r:0 發送消息。這將遍歷全部鏈接,向他們發送消息。
接下來是推送私聊,這裏我爲每一個用戶都設置了一個私有的 gid,這樣發送私信的時候也至關於發送了廣播,服務端能夠用相同邏輯處理。打個比方向 uid 20000 用戶發送私信,至關於向 u:20000 廣播。之後擴展成多進程多主機模式時,也沒必要知道這個用戶目前在哪臺服務器上,推送私信就會很方便。
在這個設計中,一位用戶是容許加入多個組的。就像羣聊同樣,一開始就使用這樣的設計,之後能夠很容易的進行擴展。
因爲採用了單點鏈接的形式,並不須要每次進房間都創建新鏈接,客戶端只要首次進行鏈接,以後發送請求就能夠進出房間,也節省了客戶端的開發工做。
2.0版本需求:
內測版本
支持擴容
更高的可用性
禮物信息比聊天優先級高
特定消息要發送回執
當1.0版本完成以後,接下來就是要開發 2.0 版本。
這個版本里增長了一些必要的需求。
首先,這是一個內測版本。在內測版本中會邀請用戶使用,並進行測試。
期間可能因爲用戶數增多,也可能由於咱們代碼自己的緣由或是 bug 形成服務器壓力增長,此時會有擴容方面的需求。
第二個是要增長廣播優先級的功能,這個很簡單,禮物確定是比普通訊息優先級更高的。
最後是特定消息的回執發送,這個咱們目前也尚未實現,可是需求一直是在的。
△ IM系統2.0版本的架構
上圖中能夠看到模塊數增長了,可是總的來講客戶端需求並無變化,基本流程的鏈接流程仍然是先問 PHP 要 token,再去鏈接 WS。因爲咱們容許把 WS 部署在多個主機上,在這中間增長了一步就是訪問 LB 得到 WS 地址。LB 會輪詢的告知客戶端本次應使用哪一個 WS,並把 IP 返回給客戶端。確保全部 WS 的壓力是平均的。
另外還有一處增長的功能是,因爲 WS 已經分離到多個進程了,那麼須要一個地方能夠用來處理廣播。這邊咱們選擇了 redis 的進行廣播。
同時另外還部署了一臺 redis 是專門用來保存在線列表等數據。
△ 帶有優先級的推送消息
上圖是一個帶有優先級的推送消息。
你們可注意到,咱們爲每一個組的gid增長了一個後綴,這個後綴是用來區分優先級的。
好比說用戶進入房間 10000,那麼我會把他放到 2 個組,分別是 r:10000._ 和 r:10000.n。
同時 node 也會向 redis 訂閱兩個同名頻道,此時服務端就能夠從 2 個頻道收到推送消息,分別是 _(普通) 和 n (優先)。
後端會有聊天信息要推送,會根據優先級,是使用普通頻道仍是優先頻道進行廣播。
node 這邊會開一個主循環,好比設置 12fps ,每一幀會優先轉發優先組(頻道)中的消息,而後纔是普通消息,根據當時壓力,將會選擇拋棄普通消息。
3.0版本需求:
業務邏輯愈來愈多,須要拆分
須要支持熱更
更好的廣播性能
優化與其餘服務的通訊
日誌系統
部署腳本
到了3.0版本時,需求愈來愈多,須要拆分。
線上有時候會有bug和作活動,會有熱更的需求。
redis 廣播有可能出現瓶頸,在這以前須要須要更好的廣播性能。
同時還須要優化與其餘服務器之間的通訊。
最後還須要須要有一個簡單的日誌蒐集器。
△ IM系統3.0版本架構
這是3.0版本的架構。你們能夠看到下面有大量的服務了。其實這些服務原來是在WebSocket裏面的,如今把它拆成不一樣的進程。
客戶端鏈接這塊沒有變化,客戶端仍然須要問 PHP 要 token,問 LB 要地址,最後再鏈接。
這個版本的 WS 功能上更加單一,只負責消息轉發。
在這個版本里,咱們作了一套 RPC 框架,用來方便調用內網各服務的接口。
增長了日誌蒐集器和API的服務。以及IM系統(私聊)。用戶之間能夠聊天和查看聊天記錄等。
總的來講,功能上沒有特別大的變化,尤爲是接口方面是向前兼容的。
△ 經過RPC中轉的推送消息
下面我介紹一下RPC服務框架。
咱們的 RPC 框架是基於 ZeroMQ 的。利用他的裏的Dealer和Router 進行消息的收發。
ZeroMQ 自帶自動重連和負載均衡功能,這方面也不用太操心,節省了開發時間。
數據交換格式是JSON,明文傳輸,調試也會很方便。
ZeroMQ 性能
-E5-2630,8G 內存,千兆網卡的測試結果
#local_thr tcp://*:5555 100 10000000
message size: 100 [B]
message count: 10000000
mean throughput: 1097051 [msg/s]
mean throughput: 877.641 [Mb/s]
還有就是咱們再也不使用 redis 作廣播,而改用 ZeroMQ 的 pub 和 sub 作廣播服務。這樣廣播性能就有了很大提高。
IM系統4.0版本的需求
廣播和業務分離
優化PRC協議
針對個別地區進行網絡優化
到了這個階段,咱們遇到了一些個別地區網絡較慢的問題。
咱們把 WS 和 LB 放到了代理服務器後面。LB 能夠根據不一樣運營商用戶,返回代理了的 WS 地址。
△ RPC服務路由
同時還優化了RPC 服務,咱們爲 RPC 服務增長了路由器,全部的 Worker 都隱藏在他後面,這樣客戶端調用的時候並不須要知道具體 Worker 的地址。Worker 的更新重啓也不會影響其餘客戶端。
廣播服務也獲得了業務和廣播分離,同時集羣化。