導語

Hermes(信使)系統是金融無線團隊打造的一個基於MQTT和Protocol Buffers的輕量級、可靠性高的移動端消息推送系統;實現了服務端消息的實時推送到客戶端的功能,並且系統輕量化、資源佔用較少、簡單易用。

背景

Hermes:赫爾墨斯,是古希臘神話中的商業、旅者、小偷和畜牧之神。也是衆神的 使者 ,奧林匹斯十二主神之一。是宙斯與阿特拉斯之女邁亞的兒子,羅馬又稱墨丘利。

在58金融業務發展過程中,由於業務場景需求,需要實現客戶端實時獲取服務端更新的數據功能,由於客戶端和服務端進行交互的過程爲:客戶端發起請求,服務端回應的過程;又由於客戶端輪詢發送請求會造成資源佔用及浪費,輪詢請求並不是一個好的方案;基於這個需求,決定實現一個輕量級、資源佔用較少、可靠性較高的消息推送系統。

經過調研,對比了各大廠家使用方案及推送相關技術點的特點,最終決定採用MQTT+ ProtocolBuffers基於長連接的數據實時推送的方案,併爲該系統命名爲:Hermes(信使)。

具體實現方案見本文,主要包括三個部分:1.技術選型、2.技術方案實踐、3.未來優化方向。

技術選型

1、方案調研

在調研過程中,發現需求功能可以使用推送來實現,但最終經過各種考慮,發現自建長連接進行實時數據推送是最爲貼合需求的實現方案;調研過程中,對推送廠家方案及本文方案做了對比,結果如下:

推送SDK 集成APNS 保持長連接
極光推送
個推
百度推送
騰訊信鴿
本文方案

從表格來看,本文方案不過是極光推送和個推的一種簡化版,完全可以使用極光推送和個推來實現,或者是使用推送來進行數據推送;但從需求具體來講,要求消息的及時性及到達率,基於這兩點的要求,以上方案均不是完美方案;具體分析如下:

  • 推送方案具有極大的不確定性,消息丟失率比較高(尤其是安卓端)。

  • iOS推送系統到達率可以理解爲100%,到達率可以完美解決,但是推送並不能保證百分百實時到達,不能達到及時性的要求。

  • 對於三方SDK採用的長連接協議並不可知,且數據傳輸格式,連接存在狀態不受控制,可能導致未知問題。

  • 三方SDK定位問題較爲困難,溝通配合成本直線上升。

  • 大量推送數據之後,難免需要付費,並且數據存在共享服務器中,服務質量是否有保障。

2、技術選型

基於以上方案的分析,自建一套輕量級、可靠性高的推送系統纔是這個需求的最終解決方案;

在技術選型的過程中,調研了MQTT及XMPP等傳輸協議,以及JSON、XML、Protocol Buffers等數據序列化協議,基於輕量級、可靠性的關鍵點,最終在其中選擇了MQTT作爲傳輸協議,Protocol Buffers作爲數據序列化協議;詳細原因如下:

2.1  MQTT

MQTT(MessageQueuing Telemetry Transport,消息隊列遙測傳輸)是一個客戶端服務端架構的發佈/訂閱模式的消息傳輸協議。它的設計思想是輕巧、開放、簡單、規範,因此易於實現。這些特點使得它對很多場景來說都是很好的選擇,包括受限的環境如機器與機器的通信(M2M)以及物聯網環境(IoT),這些場景要求很小的代碼封裝或者網絡帶寬非常昂貴。本協議運行在TCP/IP,或其它提供了有序、可靠、雙向連接的網絡連接上。它有以下特點:

  • 使用發佈/訂閱消息模式,提供了一對多的消息分發和應用之間的解耦。

  • 很小的傳輸消耗和協議數據交換,最大限度減少網絡流量。

  • 異常連接斷開發生時,能通知到相關各方。

  • 提供三種等級的服務質量:

A、QoS0:至多一次;

B、QoS1:至少一次;

C、QoS2:只有一次;

  • 消息傳輸不需要知道負載內容。

在上述特點中,特點1和特點2的存在使得MQTT協議天然具備了輕量級的特點;發佈/訂閱模式的存在,使得發佈者和訂閱者不存在直接聯繫,減少了連接通路的佔用,節省了資源;打個比方:和一位朋友聯繫,可以選擇打電話的方式,但是這樣需要電話接通才能夠開始交流,這種情況就屬於請求/回應的場景,是一個同步場景,需要一直佔用連接通路;或者可以選擇發郵件,這樣你發了電子郵件之後就可以釋放資源,去忙其他的事情,朋友隨時都可以去查看電子郵件,這就屬於發佈/訂閱場景,是一個異步場景。

另外,MQTT儘可能的簡化協議內容,最小頭部只有2字節,能儘可能的提高傳輸成功率,減少因爲網絡差而引起的消息丟失;且MQTT在設計之初,就是在物聯網低功耗、網絡帶寬有限的智能硬件上使用的,能更好的兼容低端設備。

特點4中的只有一次的服務質量策略可以保證我們消息推送的可靠性。

綜上分析,MQTT是在此方案調研中較爲適合的通訊協議。

2.2、Protocol Buffers

ProtocolBuffers,是Google公司開發的一種數據描述協議,類似於XML能夠將結構化數據序列化,可用於數據存儲、通信協議等方面。比JSON更加輕量,傳輸時消耗更小的帶寬。跨語言,這是它的一個優點。它自帶了一個編譯器--protoc,只需要用它進行編譯,就可以編譯出大部分平臺的類文件;該協議具有以下特點:

  • 小巧、便捷

  • 簡單、易用

Protocol Buffers小巧的特點,更能將MQTT傳輸的信息進行極致的壓縮,做到傳輸體積更小的目的,進一步提高了傳輸成功率;簡單易用的特點可以讓我們輕鬆的上手,集成到項目中。

故在數據序列化協議上,Protocol Buffers是比較合適的。

上述方案,因採用MQTT 和Protocol Buffers兩種協議,對弱網情況兼容較好,設備性能要求相對較低,且對數據格式優化比較好,且連接狀態完全可控,可以實時監控到鏈路是否可用,對分析問題、解決問題有很大便利。

技術方案實踐

1、方案調研

基於需求的場景,車分期的銷售用戶,需要及時知曉訂單的審覈狀態,需要服務器的審覈狀態及時傳送到客戶端,此場景是在用戶使用APP過程中回調,故在APP存活過程中保持長連接的存在是此次方案主要解決的問題;APP在被殺死之後消息的到達,可以通過接入推送系統來解決,但由於時間緊迫,系統一期優先實現方案如下,技術方案大體流程如下圖:

信使系統大致流程圖

在整個過程中,消息的實時傳送靠推送後臺和長連接SDK建立的長連接來實現;而基於本方案實現的SDK則負責和推送後臺維持長連接的有效性以及維持長連接的優化功能;

詳細流程中涉及到的交互方和交互流程參照下圖:

接入信使系統交互圖

2、方案架構

信使系統架構圖

在整個數據實時傳送方案中,涉及到長連接的建立維護、通信數據按協議封裝、業務數據封裝及優化等功能,在此對整個方案運用到的技術點放進系統架構圖中進行抽象整理,整理之後結構如下:
在整體結構中,分爲業務層、應用層、傳輸層、和網絡層,其中業務層、應用層、傳輸層組成了數據實時推送服務的SDK;
下面對整個SDK架構層級,以及各層級間包含的模塊進行詳細整理及說明;
2.1、業務層
該層主要解決,數據格式轉化,消息丟失、重複,消息存儲,數據統計等功能;下面對這些功能逐一介紹:

  • 數據格式轉化: 此模塊負責將Protocol Buffers類型的數據轉化成JSON數據。

  • 消息重複、丟失處理:此模塊會根據規則判斷收到的消息是否重複,或者在收到該信息之前是否丟過消息,經過處理之後,如果重複則不再傳給業務方,如果有消息丟失也將告訴業務方,以便業務方處理相關情況。

  • 消息存儲:根據一定緩存策略對消息進行短時間存儲,以便解決消息判重、處理丟失等問題。

  • 數據統計:此模塊將對消息進行統計,區分重複、丟失等情況,並將數據上報服務器,方便驗證整個方案的可行性和問題等。

2.2、 應用層 該層命名的來源主要來自於MQTT,因MQTT屬於業務層協議,該層的操作絕大部分是對協議的實現以及優化;主要包含心跳機制、斷線重連機制、連接狀態管理等;

  • 心跳機制:此模塊爲該方案主要技術點,下文會獨立講解。

  • 斷線重連機制:此模塊爲該方案主要技術點,下文會獨立講解。

  • 連接狀態管理:此模塊負責對應用前臺、後臺不同狀態情況下,連接的處理情況的管理。

2.3、

傳輸層

該層主要爲調用系統API對Socket數據進行拼接組裝,以及發起連接、關閉連接等操作;

對於該方案的具體模塊,以上均已介紹完畢,以下是對該方案大致流程的梳理,如下圖:

信使系統流程圖

此流程展示出了SDK從建立連接、接收數據,經由TCP數據包處理、MQTT通信數據解析,到Protocol Buffers數據格式處理等大致流程。

3、技術要點

3.1、心跳策略

說到心跳保活的策略,那麼我們可能要回到心跳包存在的的意義以及爲什麼長連接必須要有心跳包,那麼很容易就引出以下兩個問題:

  • 什麼是心跳包,心跳包的存在到底是爲了解決什麼問題;

  • 爲什麼TCP的KeepAlive不能替代應用層心跳保活機制;

首先,心跳包是爲了客戶端和服務端爲了確保對方存活而每個一定時間發送的數據包;那麼,心跳包存在的意義在哪裏呢?其實我們都知道,如果客戶端和服務器都處在同一個穩定的網絡,並且都只處理和對方連接好的長連接,那麼就算過一段比較長的時間,那麼長連接也是會存在的;但是很顯然這種情況是不會存在的,客戶端和服務端不可能只處理這一個連接,而且網絡也不可能一直處於穩定狀態,尤其在客戶端是手機這樣的移動設備的情況下,這樣很容易就帶來了以下兩個問題:

  • 移動網絡的NAT超時

由於IPv4的IP數量有限,運營商分配給手機終端的IP是運營商內網IP,手機要連接互聯網,就需要通過運營商的網關做一個網絡地址轉換(Network AddressTranslation,NAT)。簡單的說運營商的網關需要維護一個外網IP、端口到內網IP、端口的對應關係,以確保內網的手機可以跟網絡上的服務器通訊。大部分移動無線網絡運營商都在鏈路一段時間沒有數據通訊時,會淘汰NAT表中的對應項,造成鏈路中斷。

  • 網絡狀態的切換

手機網絡和WIFI網絡切換、網絡狀態不好斷開又重新連上等情況有網絡狀態的變化,這樣會使長連接變爲無效連接。需要監聽響應的網絡狀態變化事件,重新建立Push長連接。

從以上兩個問題得出,首先,心跳包的存在可以解決移動網絡NAT超時問題,並且長連接心跳包的間隔時間必須要小於NAT超時時間,如果超過NAT超時時間不做心跳,TCP長連接鏈路就會中斷,服務端就無法發送消息給手機,只能等到客戶端下次心跳失敗後,重建連接才能取到消息。其次,心跳包可以探測連接是否斷開以及客戶端和服務端的存活。

其次,TCP中的KeepAlive回去探測TCP連接的死活,那麼爲什麼不能用來替代心跳包呢?聽起來兩者似乎是一件事情,但是本質上卻有很大區別:

  • TCP中的KeepAlive是保持TCP連接的存活,也就是說它保持了連接通道的存在。

  • 心跳包不僅探測了長連接的存在,且確定了長連接兩端的客戶端和服務端的存活。

還是和朋友打電話的例子:KeepAlive可以保證你和朋友的電話是通着的,但是確不能保證你和朋友都在電話旁,而心跳包不僅保證了電話通了,而且保證了你和朋友都在接聽電話。

這樣就很好理解了,假如服務器負載過高,無法響應請求,此時TCP探針依舊認爲連接是存活的,但其實服務器已經重啓,原來的連接已經不能再給客戶端發消息了;而心跳包可以發現這個問題,並會發起重連,假如服務器在很短的時間內重啓成功,那麼還是不會影響我們消息到達的;從另一方面來說,客戶端可能已經被用戶殺死,並不在需要這個連接存在了,服務端也可以通過收不到心跳包來確定此連接已不再需要,及時的回收資源,避免資源的浪費。

基於對以上問題的探索,我們很容易得出:心跳包主要是爲了解決移動網絡NAT超時問題,那麼從這個點出發,我們就可以得出系統心跳時間間隔的決定因素:各大運營商NAT超時時間。

只要心跳包間隔小於這個NAT超時時間,那麼我們就可以最大可能的節省資源消耗的前提下,保證連接的可用性。

據網絡查詢出的結果可得到部分運營商NAT超時時間:

運營商 NAT超時時間
移動 5分鐘
聯通 5分鐘
電信 大於28分鐘

根據以上數據,結合需求需要實時性較高,用戶需要得到的信息一般在一兩分鐘就會返回結果,故方案現採用120s的時間間隔作爲心跳頻率。

3.2、斷線重連機制

因各種不確定情況的發生,連接不可能一直存在,故需要設計斷線重連機制,需要從以下兩方面考慮:

  • 因存在網絡切換、移動網絡基站變更、以及網絡斷開等問題。

  • 服務器異常導致的斷線重連,如負載過高;爲了防止服務器異常斷線之後,造成客戶端集體掉線的情況,客戶端會立刻發起重連,在用戶量較小的情況下,客戶端集體重連對於服務端可以承受,但當用戶量級達到一定數量,大量客戶端的重連,可能就會產生重連風暴,直接導致服務端再次出現異常,因此對於重連機制也應考慮一個比較合適、能避免重連風暴的策略。

在這裏,採用了將重連間隔隨機分配在15s內進行,也就是說客戶端在檢測到連接斷開之後,會隨機的在15s以內的一個時間間隔進行重連,這樣就可以將重連風暴進行削峯,大大的減輕了服務器的壓力,避免了服務器再次出現異常。

爲了避免服務器一直異常,客戶端卻一直重試造成電量、流量的浪費,重連機制會選取6次1~15s之間的隨機時間間隔進行重連,減少了電量、流量的消耗,提升用戶體驗。

到此,整個現有方案結束。

未來優化方向

考慮到移動端手機電量、流量、及網絡連接不穩定的情況,上述方案可以滿足現實需求,但顯然不是完美的方案,針對上述方案,現有以下規劃:

1、實現殺死APP的情況下消息的推送 爲進一步提升用戶體驗,完善推送系統,實現APP在殺死的情況下,也可以接收到推送消息,對於iOS開發中來說,由於蘋果公司的限制,在APP沒有啓動的情況下,只能通過APNS來進行推送,故,後續接入蘋果APNS即可實現該功能,完善之後的流程圖如下:

推送流程圖

2、智能心跳策略 因心跳是保證連接存活的必要手段,心跳的存在毋庸置疑,但是心跳的存在,也必定會增加電量、流量的消耗;故心跳間隔需要在可以保證NAT不超時的情況下儘可能的久,因此我們可以從一個固定心跳入手,去儘可能的探測心跳的最大步伐,去減少流量和電量的消耗。

總結

本文通過信使系統的實踐過程,總結出信使系統的架構,以及在過程中遇到的技術點,以及一些解決方案,在實踐過程中,不僅解決了業務需求問題,並在一定程度上提升了用戶體驗;經過線上驗證,信使系統消息到達率達99%以上,且消息及時性較爲可靠,很好的滿足了車分期項目的使用;但是一個系統從來都不是一蹴而就的,都要經過不停的迭代與優化才能越做越好,這個過程將會是一個漫長且繁瑣的過程,爲了支撐業務完美運行,並且給用戶以更好的體驗,我們更應該嚴格要求,設計更好地優化方案,提高編碼質量,使系統日趨完美。

參考文獻

1、MQTT官方文檔:http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html

2、Protocol Buffers官方主頁:https://github.com/52im/protobuf

3、MQTTClient開源庫:https://github.com/novastone-media/MQTT-Client-Framework

作者簡介

張鴻運:金融公司-技術中心-無線技術部,iOS開發高級工程師,主要負責金融公司消費金融業務線和金融無線技術部iOS基礎服務的建設。

閱讀推薦

相關文章