一、解決什麼問題 + 難點
解決什麼業務問題
端到雲的實時上報需求:58速運司機端gps實時上
雲到端的實時推送需求:58速運司機訂單實時推送
(3)端到端的聊天消息需求:使用者、商戶、客服之間的聊天溝通
難點:
(1)app無線環境下消息可達性<
(2)通用性,平台實作盡量與業務解耦
二、傳統解決方案與潛在不足
【端到雲:http輪詢上報gps消息】
方案一:直接通過業務線web-server寫db
方案二:通用web-server層調用業務服務層寫db
潛在不足:
(1)http短連接配接代價高(反複建立與銷毀連接配接)
(2)web-server層吞吐量較低(每秒處理千級别請求)
【雲到端:通過第三方push或者推送服務】
方案一:通過apns或者米推等第三方推送
方案二:通過自己搭建mqtt服務推送
(1)第三方可達性與實時性無法保證,第三方會進行推送限速
(2)mqtt可用性是個問題
【端到端:結合上面兩種方法實作】
傳統方案往往可以通過結合【端到雲】與【雲到端】來結合解決【端到端】的實時消息推送問題。
三、通用實時消息平台實作細節
業務的分析與抽象:司機、使用者、商家、客服均為“線上”業務 【端到雲的優化】
傳統方案潛在的問題:http輪詢效率不高,web-server性能有限 優化tips:消息平台使用tcp長連接配接(如上圖)
潛在的問題:消息平台與業務線app-server耦合,需要switch case業務線類型來分發投遞消息,新增業務線需要新增rpc調用(如上圖) 優化tips:使用消息總線msg-queue解耦(如下圖)
可以看到,使用消息總線後,新增消息發送方,消息平台隻需要配置消息類型與消息總線主題的映射關系,新增的app-server消費方訂閱新的主題即可,實作消息平台與業務的解耦。
【雲到端的優化】
潛在的問題:可用性問題與第三方限速優化tips:自己提供消息平台叢集,提供rpc接口,實作“雲到端”的消息通道
這裡要注意的是,“端到雲”使用消息總線,是為了業務解耦。“雲到端”直接使用rpc接口,也是為了業務解耦,新增消息推送方,消息平台無需改動代碼。
潛在的問題:不少司機推送訂單無回複,搶單率比預期的低
優化tips:引入狀态實時存儲,隻有“線上”狀态的使用者才推送消息
如果業務無關,則直接通過tcp通道投遞;如果業務相關,發送方先來一個“端到雲”的投遞(通過mq),業務伺服器處理再反向來一個“雲到端”的投遞(rpc)給接收方。
潛在問題:如果接收方不線上怎麼辦
優化tips:增加db存儲離線消息
潛在的問題:無線環境下經常網絡不穩(例如進出電梯斷網),消息經常丢失
優化tips:消息平台收到消息先落地資料庫,接收方收到後應用層ack再删除,以保證不丢失
如上圖(本文最重要的2張圖之一),整個消息投遞流程為:
(1)發送發将消息發給消息平?
(2)消息平台先将消息落地db
(3)消息平台回複發送方消息發送成功(此時和接收方是否接到無關)
(3)與此同時,并行的把消息投遞給接收方(如果不線上就存離線了)
(4)接收方應用層ack表示收到了消息
(5)消息平台将消息删除
(6)告之接收方ack已經成功處理
可以看到,是使用“應用層ack來解決消息可達性問題的”
潛在問題:發送方沒有收到第3步驟中的消息平台回複怎麼辦?
優化tips:發送方重發(伺服器無狀态)
潛在問題:接收方收到重發的備援消息怎麼辦?
優化tips:接收方去重(可以做到服務端完全無狀态,隻需要簡單投遞消息即可)
【分層架構說明】
整個系統的分層架構如上圖(本文最重要的2張圖之二),整個消息平台系統由:
(1)消息平台在app裡的msg-sdk,向app提供帥氣的接口
(2)msg-gate,整個消息平台的tcp接入門戶,保持tcp長連接配接,初步攻防,加解密,壓縮解壓縮
(3)msg-logic,整個消息平台邏輯處理的部分
(4)redis,高可用redis叢集存儲使用者線上狀态online/offline,以及使用者在哪一台msg-gate接入(如果線上)
(5)db,存儲離線消息
非消息平台的幾個業務部分:
(1)app:業務方app,可以有多個,通過msg-sdk來接入消息平台
(2)mq:消息平台通過mq來給業務方伺服器發“端到雲”的消息
(3)app-server:業務方後端,可以有多個,通過mq接收“端到雲”的消息,通過rpc發送“雲到端”的消息
【對外提供的接口說明】
消息平台對業務方提供的接口是很少很通用的接口。
msg-sdk對app提供的核心接口有:
1)login:接入消息平台
(2)logout:登出消息平台
(3)c2s:發送client to server“端到雲”的消息
(4)c2c:發送client to client“端到端”的消息
(5)get-offline-msg:拉取離線消息
(6)on-msg-recieved:收到消息的callback回調接口
消息平台對app-server提供的核心接口有:
(1)s2c:發送server to client“雲到端”的消息
其他業務方不需要關注,是msg-sdk與消息平台之間的内部接口有:
(1)keepalive:用于msg-sdk與消息平台的連接配接保持(對業務方透明)
(2)c2c-ack:使用者c2c接口的應用層ack接口(對業務方透明)
【如何實作跨帳号體系的聊天】
既然是通用的消息平台,如何實作跨帳号體系的消息發送呢(即如何實作qq與旺旺的聊天)?
解決方案:不再使用uid作為整個系統運作的key,而使用domain+uid,或者appid+uid來作為整個系統運作的key
潛在耦合點:這樣的話,login接口的邏輯處理,消息平台需要switch case (domain或者appid)來進行不同的登入驗證,與業務有一定的耦合,不過新增帳号體系的頻度很低,遠比新增消息類型低
【協定的擴充性設計】
app本質是cs架構,一旦放出去的版本就很難收回來,其相容系要求遠比bs架構難,如何做到新增功能的同時,還能友善的相容曆史舊版app呢?
(1)如何友善的增加接口?
解決方案:協定使用定長標頭 + 變長包體,使用指令号cmd來擴充新接口【這個變化對業務層是透明的,是msg-sdk與消息平台之間的事情】
(2)對于同一個接口,能否增加參數,而不影響舊版本的app?
解決方案:使用可擴充的序列化協定,例如protobuffer【protobuffer這個東西也對業務線透明】
(3)對于業務方,有很多種類的消息類型,有很多複雜的業務需求,如何保證業務擴充性的同時,又不會增加消息平台的複雜性,并對舊版本app相容?
例如業務線可能有這樣的潛在需求:
a)推送一個營運消息
b)推送消息内容支援字型、字号、加粗、顔色
c)推送消息支援圖檔
d)業務支援“視窗震動”,以及“對方正在輸入......”等需求
解決方案:使用可擴充的消息體協定(對消息平台透明),例如xml/json來支援可擴充的多樣消息類型,并對舊版本app相容
使用這種消息内容協定,能保證:擴充性好、舊版本相容、對消息平台透明等諸多好處,強烈建議使用
四、分布式架構細節
抱歉,主持人提醒時間已到,分布式架構擴充性、負載均衡性、可用性、一緻性的問題線下和大家分享,先放一個分布式架構圖吧:
如果你讀到了這裡,說明對消息平台還是有點興趣的,58到家消息平台團隊隻有一個同學,奇缺人,歡迎有志之士加入。
一些說明:base北京,核心團隊,技術導向,java方向,和58沈劍一個團隊
加入方式:(1)直接在文章下面留言(2)在公衆号回複“招聘”擷取加盟密鑰
五、總結
(1)“端到雲”消息投遞:tcp消息通道,消息總線業務解耦
(2)“雲到端”消息投遞:提供rpc接口,引入狀态存儲
(3)“端到端”消息投遞步驟如下圖:
“端到端”消息投遞
a)先存離線消息防丢失
b)ack機制保證可達
c)發送方消息重發
d)接收方消息去重
5)可擴充協定設計
a)定長標頭,變長包體,随時增加接口
b)可擴充序列化協定,随時變化接口
c)可擴充消息協定,随時增加類型
6)支援跨帳号體系聊天(多個域):使用domain(或者appid)+uid作為綜合key
(7)分層架構如下圖