1、前言
隨着IOS,Android應用的普及,對推送消息的需求也越來來重要,目前市面上有的個推推送、極光推送、友盟推送等等,那如何自己搭建一個推薦系統昵?
2、名詞說明:
設備端:消息接收方;
消息服務:負責和設備端建立連接,並推送消息給設備端
消息系統: 消息服務集群;
路由系統:根據消息服務的負責返回合適的消息服務給設備端(主要負責監控當前可用的消息服務、監控當前消費服務的負載情況,返回合適的消息服務給設備端)
業務系統:消息系統之外的其它業務系統;
平台:包括設備端和前端頁面系統;
3、設計原則
可擴展性:為了保證系統的高可用,要求各模塊都可以水平擴展;
高可用性:設計過程中必須考慮系統的穩定性;
原則單一:各組件的功能的單一(如:消息系統只負責和設備端連接和向設備端發送消息,禁止在消息系統中做復雜的業務邏輯)
4、技術選型
2.1:傳輸方式
要滿足大量的連接數、同時支持雙全工通信,並且性能也得有保障。
在 Java 技術棧中進行選型首先自然是排除掉了傳統 IO
。
那就只有選 NIO 了,在這個層面其實選擇也不多,考慮到社區、資料維護等方面最終選擇了 Netty。
2.2:數據格式選型
1、json 簡單
2、hession(為了簡單說明,該文檔就以hession為主)
3、protobuf
4、自定義協議
5、架構圖
說明:
5.1、服務端啟動說明(上面紅色箭頭標記)
啟動包括兩部分
1)注冊服務到zk中;
2)監聽MQ消息中,本機到消息。
5.2、客戶端啟動流程
客戶端啟動包括五部分
1)請求路由系統獲取可用到消息服務;
2)路由系統獲取可用到消息服務;
3)路由系統通過redis獲取可用消息服務的負載,路由系統根據消息服務的負載返回合適的消息服務給設備端;
4)設備端連接消息服務;
5)消息服務接受連接服務,進行驗證消息,向Redis中記錄連接信息。
5.3、啟動過程中需要考慮的幾個問題
1)保證連接的穩定可靠(心跳)設備端和消息服務都進行必要的心跳檢查。
當一台智能手機連上移動網絡時,其實並沒有真正連接上Internet,運營商分配給手機的IP其實是運營商的內網IP,手機終端要連接上Internet還必須通過運營商的網關進行IP地址的轉換,這個網關簡稱為NAT(NetWork Address Translation),簡單來說就是手機終端連接Internet 其實就是移動內網IP,端口,外網IP之間相互映射。
GGSN(GateWay GPRS Support Note)模塊就實現了NAT功能,由於大部分的移動無線網絡運營商為了減少網關NAT映射表的負荷,如果一個鏈路有一段時間沒有通信時就會刪除其對應表,造成鏈路中斷,正是這種刻意縮短空閑連接的釋放超時,原本是想節省信道資源的作用,沒想到讓互聯網的應用不得以遠高於正常頻率發送心跳來維護推送的長連接。以中移動的2.5G網絡為例,大約5分鍾左右的基帶空閑,連接就會被釋放。
由於移動無線網絡的特點,推送服務的心跳周期並不能設置的太長,否則長連接會被釋放,造成頻繁的客戶端重連,但是也不能設置太短,否則在當前缺乏統一心跳框架的機制下很容易導致信令風暴(例如微信心跳信令風暴問題)。具體的心跳周期並沒有統一的標准,180S也許是個不錯的選擇,微信為300S,本系統選擇120s
設備端心跳檢查流程
消費服務心跳流程:
說明:設備端和消息服務心跳過程中可能存在幾個問題
1、消息不停端斷開和重連
原因:設備端和消息服務相互斷開重連;
解決方法:在redis中記錄設備新建連接時間,如果連接是新建的,消費服務心跳次數檢查調整為12次。
2、在redis中通一個客戶端對應多個消費服務
原因:redis中消費連接信息按5分鍾過期,心跳120s中進行一次,有可能斷開連接沒有清理完成前,設備端已經連接到另一個消費服務上了;
解決辦法:在連接信息中記錄最新更新信息,以最新更新信息到設備端為准(如果redis記錄信息Key能定義唯一,不存在此問題)。
3、消息服務和redis中連接信息不一致
原因:由於不可預知原因
解決辦法: redis中消費連接信息過期時間為5分鍾。
情況1:5分鍾之后,消費服務客戶端不會把消息推送到掛掉的消息服務服務器。
情況2: 5分鍾以內的消息,
1、如果消息系統掛起,說明連接已經斷開,設備端會重新建立連接,在建立連接時,設備端傳入上次最后一條消息,系統推送上次最后一條消息之后的所有消息給設備端;
同時,設備端接受到消息,根據消息發送到時間和消息id進行去重和重排序,保證消息到唯一和順序性;
2、消息服務如果收到本地連接不存在的消息,重新推送消息到MQ中;
4、連接建立和連接斷開的業務處理邏輯和消息系統耦合
原因:連接建立和連接斷開 只有消息系統清楚;
解決辦法:消息系統建立連接和斷開連接發送消息給MQ,業務系統監控MQ,進行業務處理。
5、消息系統掛機,redis中包含連接信息;
解決辦法:監聽zk中節點事件,當節點下線之后,同時刪除redis中連接信息
5.4、發送消息
發送消息流程圖:
說明:
平台:包括設備端,頁面,各種第三方服務。
步驟:
1、平台根據業務需求請求業務系統(如:手機向電視發送消息,發起視頻通話)
2、業務系統處理完業務邏輯,調用消息服務客戶端;
3、消息服務客戶端處理消息
3.1、獲取消息應該發送到哪個topic下(如果獲取不到相應到topic只寫數據(可能設備端已經下線),不寫MQ);
3.2、保存消息到mysql數據庫中(只有消息寫入數據庫成功才返回平台消息發送成功);
3.3、向MQ中寫入消息;
4、消費服務監聽MQ消息,推送消息給設備端 (如果消息不在當前線程中,則重新用消息服務客戶端 發送到MQ中,如果連續發送3次都沒找到設備端,(設備端已下線)直接丟棄消息),
回寫消息狀態到mysql數據庫
6、 監控系統
監控指標:1、各服務消息的系統性能;2、各服務消息的連接數;3、各服務消息的推送消息數據和推送的消息結果,4、推送消息日志(可以用log4j接入elk) ,5、路由系統的監控;