移動(dòng)互聯(lián)網(wǎng)蓬勃發(fā)展的今天,,大部分手機(jī) APP 都提供了消息推送功能,如新聞客戶端的熱點(diǎn)新聞推薦,,IM 工具的聊天消息提醒,,電商產(chǎn)品促銷(xiāo)信息,企業(yè)應(yīng)用的通知和審批流程等等,。推送對(duì)于提高產(chǎn)品活躍度,、提高功能模塊使用率、提升用戶粘性,、提升用戶留存率起到了重要作用,,作為 APP 運(yùn)營(yíng)中一個(gè)關(guān)鍵的免費(fèi)渠道,對(duì)消息推送的合理運(yùn)用能有效促進(jìn)目標(biāo)的實(shí)現(xiàn),。 推送最早誕生于 Email 中,,用于提醒新的消息,,而移動(dòng)互聯(lián)網(wǎng)時(shí)代則更多的運(yùn)用在了移動(dòng)客戶端程序,。要獲取服務(wù)器的數(shù)據(jù),通常有兩種方式:第一種是客戶端 PULL(拉)方式,,即每隔一段時(shí)間去服務(wù)器獲取是否有數(shù)據(jù),;第二種是服務(wù)端 PUSH(推)方式,服務(wù)器在有數(shù)據(jù)的時(shí)候主動(dòng)發(fā)給客戶端,。 很顯然,,PULL 方案優(yōu)點(diǎn)是簡(jiǎn)單但是實(shí)時(shí)性較差,我們也可以通過(guò)提高查詢頻率來(lái)提高實(shí)時(shí)性,,但這又會(huì)造電量,、流量的消耗過(guò)高,反之 PUSH 方案基于 TCP 長(zhǎng)連接方式實(shí)現(xiàn),,消息實(shí)時(shí)性好,,但是由于要保持 APP 客戶端和服務(wù)端的長(zhǎng)連接心跳,也會(huì)帶來(lái)額外的電量和流量消耗,。因此在整體架構(gòu)設(shè)計(jì)中需要折中平衡,,目前主流的推送實(shí)現(xiàn)方式都是基于 PUSH 的方案。 目前移動(dòng)推送技術(shù)實(shí)現(xiàn)方式主要有以下三種: 客戶端和服務(wù)器定期的建立連接,,通過(guò)消息隊(duì)列等方式來(lái)查詢是否有新的消息,,需要控制連接和查詢的頻率,頻率不能過(guò)慢或過(guò)快,,過(guò)慢會(huì)導(dǎo)致部分消息更新不及時(shí),,過(guò)快會(huì)消耗更多的資源(流量、電量等),對(duì)用戶體驗(yàn)有較大傷害,。 通過(guò)短信發(fā)送推送消息,,并在客戶端植入短信攔截模塊(主要針對(duì) Android 平臺(tái)),可以實(shí)現(xiàn)對(duì)短信進(jìn)行攔截并提取其中的內(nèi)容轉(zhuǎn)發(fā)給 App 應(yīng)用處理,,這個(gè)方案借助于運(yùn)營(yíng)商的短消息,,能夠保證最好的實(shí)時(shí)性和到達(dá)率,但此方案對(duì)于成本要求較高,,開(kāi)發(fā)者需要為每一條 SMS 支付費(fèi)用,。 移動(dòng) Push 推送基于 TCP 長(zhǎng)連接實(shí)現(xiàn), 客戶端主動(dòng)和服務(wù)器建立 TCP 長(zhǎng)連接之后, 客戶端定期向服務(wù)器發(fā)送心跳包用于保持連接, 有消息的時(shí)候, 服務(wù)器直接通過(guò)這個(gè)已經(jīng)建立好的 TCP 連接通知客戶端,。盡管長(zhǎng)連接也會(huì)造成一定的開(kāi)銷(xiāo),,對(duì)于輪詢和 SMS 方案的硬傷來(lái)說(shuō),目前已經(jīng)是最優(yōu)的方式,,而且通過(guò)良好的設(shè)計(jì),,可以將損耗降至最低。不過(guò),,隨著客戶端數(shù)量和消息并發(fā)量的上升,,對(duì)于消息服務(wù)器的性能和穩(wěn)定性要求提出了非常大的考驗(yàn)。因此,,就難度而言,,此方式代價(jià)最高。 基于 TCP 長(zhǎng)連接的方式是主流的推送方式,,基于該推送方式逐步發(fā)展出系統(tǒng)級(jí),、應(yīng)用級(jí)一系列的推送解決方案。 iOS 在系統(tǒng)層面與蘋(píng)果 APNs(Apple Push Notification service)服務(wù)器建立連接,,應(yīng)用通過(guò)觀察者模式向 ioS 系統(tǒng)注冊(cè)關(guān)注的消息,,系統(tǒng)收到 APNs Server 消息后轉(zhuǎn)發(fā)到相應(yīng)的應(yīng)用程序,整個(gè)過(guò)程很清晰,,并且所有 APP 都共用同一個(gè)系統(tǒng)級(jí)的連接,,減少了系統(tǒng)開(kāi)銷(xiāo),雖然 APNs 能無(wú)障礙的訪問(wèn),,但實(shí)際使用過(guò)程中,,發(fā)現(xiàn)延時(shí)和丟消息的情況偶有發(fā)生。 Android 的 C2DM(Android Cloud to Device Messaging)采取與 iOS 類似的機(jī)制,,都是由系統(tǒng)層面來(lái)支持消息推送,,但是由于 Google 的服務(wù)在國(guó)內(nèi)不能穩(wěn)定的訪問(wèn),此方案對(duì)于中國(guó)用戶來(lái)說(shuō)基本是無(wú)法使用的,。 除了 Google 官方提供的方案,,中國(guó)眾多的手機(jī)廠商在其定制的系統(tǒng)中也內(nèi)置了推送功能,如小米、華為等,。 鑒于 Android 平臺(tái) C2DM 推送的不可用性,,國(guó)內(nèi)涌現(xiàn)出大量的第三方推送服務(wù)提供商,采用第三方推送服務(wù)的系統(tǒng)流程如下圖: 圖 1:消息推送流程 目前應(yīng)用最為廣泛的第三方推送服務(wù)提供商包括個(gè)推,、極光,、友盟、小米,、華為,、BAT 等,絕大部分 APP 都會(huì)優(yōu)先考慮采用第三方推送服務(wù),。 第三方服務(wù)在開(kāi)發(fā)成本和消息到達(dá)率上表現(xiàn)都不錯(cuò),,但所有信息會(huì)經(jīng)過(guò)第三方服務(wù)器,對(duì)于信息敏感類 APP 而言,,有必要考慮自建一套消息推送服務(wù),,能最大化保證安全,但對(duì)于自建推送服務(wù),,如果從零開(kāi)始來(lái)做需要解決幾個(gè)難點(diǎn): 第一,,移動(dòng)推送服務(wù)器對(duì) App 客戶端海量長(zhǎng)連接的維護(hù)管理。第二,,App 客戶端如何保證 Push Service 常駐,,對(duì)于 Android 我們可以通過(guò)發(fā)現(xiàn) push service 不存在可以定時(shí)拉起的方式,。第三,,通信協(xié)議的制定,我們可以采用開(kāi)源的 XMPP 方式實(shí)現(xiàn),,也可以自定義協(xié)議,,不管哪種方式我們都要保證消息傳送的到達(dá)率的準(zhǔn)確性。第四,,在移動(dòng)互聯(lián)網(wǎng)網(wǎng)絡(luò)環(huán)境下,,經(jīng)常出現(xiàn)弱網(wǎng)環(huán)境,特別是 2G,、3G 等網(wǎng)絡(luò)不穩(wěn)定的情況下,,如果保證消息在弱網(wǎng)環(huán)境下不重、不丟也是一個(gè)挑戰(zhàn),。 無(wú)論是第三方推送服務(wù),,還是自建推送服務(wù),在實(shí)際的使用過(guò)程中,,發(fā)現(xiàn)都存在以下問(wèn)題:
為了解決以上問(wèn)題,我們考慮基于第三方消息推送服務(wù)構(gòu)建一套移動(dòng)消息推送中間件平臺(tái),,該消息平臺(tái)采用了低耦合的分層架構(gòu)設(shè)計(jì)(如圖 2 所示),,分為三層:接入層、傳輸層和應(yīng)用層,。其中接入層是業(yè)務(wù)方調(diào)用的入口,,我們采用異步消息隊(duì)列的方式提供了較高的業(yè)務(wù)系統(tǒng)發(fā)送消息的速度,并且具備了消息緩沖功能,,即使高峰期的海量消息推送對(duì)整個(gè)平臺(tái)沖擊較少,,保護(hù)了推送系統(tǒng); 傳輸層會(huì)從接入層接收消息并進(jìn)行解析,,對(duì)推送消息進(jìn)行合法性檢查校驗(yàn),,如果消息不合法直接丟棄,同時(shí)將合法的消息進(jìn)行協(xié)議轉(zhuǎn)換并發(fā)送到對(duì)應(yīng)的第三方推送平臺(tái),;應(yīng)用層主要是提供統(tǒng)一的 SDK 供業(yè)務(wù)使用,,封裝適配第三方推送平臺(tái)的 SDK 接口到統(tǒng)一的接口 SDK 中,這樣業(yè)務(wù) APP 使用方只關(guān)注統(tǒng)一封裝的 SDK 即可實(shí)現(xiàn)業(yè)務(wù)消息的操作,,而不需要考慮各種濾重,、校驗(yàn)等通用操作。主要功能包括:
整個(gè)系統(tǒng)設(shè)計(jì)由三部分組成:移動(dòng)推送平臺(tái),、客戶端 SDK,、應(yīng)用管理界面(第三方推送服務(wù)和自建推送服務(wù)統(tǒng)稱為推送服務(wù))。 圖 2:系統(tǒng)架構(gòu) 移動(dòng)推送平臺(tái)提供統(tǒng)一的服務(wù),,對(duì)于應(yīng)用層屏蔽推送服務(wù)接口,,且實(shí)現(xiàn)推送服務(wù)可動(dòng)態(tài)輪替。推送平臺(tái)將接收到的消息持久化到數(shù)據(jù)庫(kù)中,,方便進(jìn)行消息推送失敗后的重發(fā),,以及后續(xù)數(shù)據(jù)的統(tǒng)計(jì)分析。 客戶端 SDK 對(duì) App 提供統(tǒng)一的使用接口,,屏蔽推送服務(wù) SDK 使用細(xì)節(jié),,且實(shí)現(xiàn)多種推送 SDK 可替換,隱藏 SDK 復(fù)雜的接入過(guò)程,,方便使用,。 應(yīng)用管理系統(tǒng)面向 App 開(kāi)發(fā)人員,實(shí)現(xiàn)應(yīng)用申請(qǐng),,推送服務(wù)配置,,消息查詢與管理,數(shù)據(jù)統(tǒng)計(jì)與分析,。 消息推送涉及的主要模塊是消息推送平臺(tái)和客戶端 SDK,,主要流程如下圖所示: 圖 3:消息推送中間件核心流程 正常情況下,消息推送過(guò)程如下:
對(duì)于推送過(guò)程中可能出現(xiàn)的異常情況,,總結(jié)如下:
根據(jù)消息發(fā)送流程,,可以得到消息在生命周期中狀態(tài)的變遷如下圖: 圖 4:消息狀態(tài)機(jī) 消息重發(fā)主要存在三種場(chǎng)景:系統(tǒng)啟動(dòng)時(shí),查詢所有的發(fā)送失敗或發(fā)送成功未收到客戶端回執(zhí)的消息,,加載到推送隊(duì)列重發(fā),;系統(tǒng)運(yùn)行時(shí),后臺(tái)線程定時(shí)查詢需要重發(fā)的消息,,進(jìn)入推送隊(duì)列,;手動(dòng)觸發(fā)時(shí),直接將消息加入推送隊(duì)列,。 由于消息推送中間件服務(wù)通常要求高可用,,為分布式部署,消息重發(fā)必須保證在單一節(jié)點(diǎn)執(zhí)行,,且保證只發(fā)送一次,。需采用分布式鎖的方式,保證重發(fā)只發(fā)一次,,主流實(shí)現(xiàn)方式有三種:
對(duì)于每種鎖機(jī)制的特點(diǎn)本文不詳細(xì)介紹,,根據(jù)實(shí)際應(yīng)用需要任選一種即可,。 由于 iOS 平臺(tái)和 Android 平臺(tái)的差異,消息重發(fā)需要考慮平臺(tái)差異性,。 使用第三方推送時(shí),,如果 iOS 應(yīng)用在前臺(tái)運(yùn)行,那么將通過(guò)第三方推送維護(hù)的長(zhǎng)連接,,以透?jìng)鞯姆绞街苯酉掳l(fā)到 APP,,稱為應(yīng)用內(nèi)消息;而當(dāng) APP 在后臺(tái)時(shí),,則第三方推送將消息推送到 APNs,,由 APNs 推送到 APP,稱為 APNs 通知,。當(dāng)通過(guò) APNs 推送時(shí),,手機(jī)在收到消息后將在頂部的通知欄出現(xiàn)相關(guān)推送內(nèi)容,這一行為是系統(tǒng)級(jí)別的,,APP 無(wú)法控制,。可能會(huì)出現(xiàn)這一問(wèn)題:當(dāng) APP 在后臺(tái)或者手機(jī)鎖屏的情況下,,如果服務(wù)端重發(fā)了消息,,手機(jī)的通知欄將出現(xiàn)多條通知,。 因此,考慮當(dāng) APP 在后臺(tái)時(shí),,針對(duì) iOS 平臺(tái)的消息不再進(jìn)行重發(fā),;只有當(dāng) APP 進(jìn)入前臺(tái),才重新進(jìn)行重發(fā),。APP 的活動(dòng)狀態(tài)通過(guò)第三方推送服務(wù)的 api 可以獲取到,。 Android 平臺(tái)不存在該問(wèn)題。 由于消息重發(fā)可能會(huì)造成客戶端收到重復(fù)消息,,需要在客戶端進(jìn)行消息去重,。服務(wù)端為每一條消息分配了一個(gè)唯一 id,重發(fā)時(shí)唯一 id 不變,??蛻舳诵枰4媸盏降拿恳粭l消息,在接收到新消息時(shí)首先根據(jù)唯一 id 判斷是否已經(jīng)收到了這條消息,,如收到則不響應(yīng),。客戶端保存消息可以采用 sqlite 數(shù)據(jù)庫(kù),。 客戶端 SDK 與服務(wù)端的通信過(guò)程使用 appKey 和 appSecret 進(jìn)行權(quán)限控制,。appKey 是服務(wù)端為每個(gè) app 分配的唯一標(biāo)識(shí),appSecret 是服務(wù)端為每個(gè) app 分配的秘鑰,。 客戶端 SDK 在請(qǐng)求服務(wù)端 HTTP 接口時(shí),,會(huì)將 appKey appSecret 做一次簽名,將簽名值作為簽名 sign 參數(shù),,與其他請(qǐng)求參數(shù)(業(yè)務(wù)參數(shù) appKey)一同傳到服務(wù)端,;服務(wù)端拿到請(qǐng)求參數(shù)后,也先用 appKey appSecret 做一次簽名,,比較和客戶端傳來(lái)的 sign 參數(shù)是否一致,,從而完成權(quán)限驗(yàn)證過(guò)程。為了能夠?qū)崿F(xiàn)靈活控制推送與否,,可實(shí)現(xiàn)黑名單管理的功能,。處于黑名單內(nèi)的 app 客戶端不再進(jìn)行消息的推送。黑名單控制的粒度到賬號(hào)級(jí)別,,也可以根據(jù)實(shí)際業(yè)務(wù)需要進(jìn)行分組管理,。 在某些業(yè)務(wù)場(chǎng)景中,需要對(duì)消息進(jìn)行過(guò)濾,,分析,,做出相應(yīng)的處理甚至預(yù)警,借助于消息推送平臺(tái),都能方便的實(shí)現(xiàn),。 客戶端 SDK 是基于推送服務(wù)的 SDK 封裝實(shí)現(xiàn),,對(duì)外提供統(tǒng)一的使用接口,。SDK 的使用者不再關(guān)注具體使用了哪些第三方推送,、推送服務(wù)的接入細(xì)節(jié)。實(shí)現(xiàn)與推送服務(wù)的充分解耦,,降低開(kāi)發(fā)和使用成本,。 由于 iOS 和 Android 平臺(tái)的差異性,在客戶端 SDK 的封裝上存在差異,,下面分別介紹兩個(gè)平臺(tái)的 SDK 封裝方式,。 SDK 提供啟動(dòng)和停止的方法;同時(shí)定義一個(gè) protocol,,包含 SDK 提供的接口,。SDK 在收到消息或出現(xiàn)錯(cuò)誤時(shí)將會(huì)回調(diào) protocol 中的接口。 由于推送的接入涉及 AppDelegate 的生命周期方法,,為避免 SDK 使用者關(guān)注這些繁瑣的細(xì)節(jié),,SDK 使用 Aspects 的方式,將推送時(shí)相應(yīng)的處理函數(shù) hook 到 AppDelegate 的生命周期方法上,。 在 Android 中使用 Receiver 組件來(lái)接收收到的消息,。一個(gè)基本的配置如下所示: 流程如下:當(dāng)推送服務(wù)的 SDK 在接收到推送過(guò)來(lái)的消息后,將發(fā)送廣播,,這個(gè)廣播的用 intent-filter 標(biāo)識(shí),,當(dāng)應(yīng)用中的 Receiver 代碼注冊(cè)了這個(gè) intent-filter,就可以接收到廣播,,并進(jìn)行后續(xù)處理,。 圖 5:后臺(tái)管理示意圖 消息后臺(tái)管理系統(tǒng)提供應(yīng)用申請(qǐng)、應(yīng)用服務(wù)配置,、推送服務(wù)配置,、消息查詢與管理等功能。 1,、應(yīng)用申請(qǐng) 填寫(xiě)應(yīng)用名,、應(yīng)用描述等信息后,生成該應(yīng)用唯一的 appKey 和 appSecret,。 2,、應(yīng)用服務(wù)配置 為應(yīng)用選擇要使用的移動(dòng)端通用服務(wù),可供選擇的有推送,、反饋,、版本發(fā)布。 3、推送服務(wù)配置 為應(yīng)用配置推送服務(wù),,可供選擇個(gè)推,、極光等;以及推送時(shí)使用的優(yōu)先級(jí)順序,。 4,、消息查詢與管理 查看應(yīng)用所發(fā)出的消息,包括消息所屬應(yīng)用,、所屬賬號(hào),、消息的狀態(tài)、最終發(fā)送成功的第三方渠道,、消息的來(lái)源,、發(fā)送者 ip 等信息 5、數(shù)據(jù)統(tǒng)計(jì) 通過(guò)分析 message 表中的各消息的狀態(tài),,可統(tǒng)計(jì)各應(yīng)用消息的發(fā)送成功率和到達(dá)率,,以及哪個(gè)第三方推送的更優(yōu),方便選擇,。同時(shí),,提供每日、每周,、每月推送消息量的統(tǒng)計(jì),,并提供統(tǒng)計(jì)圖表。 消息推送平臺(tái)通過(guò)無(wú)狀態(tài)設(shè)計(jì)、統(tǒng)一存儲(chǔ),、冗余部署方式保證了高可用,,對(duì)應(yīng)的狀態(tài)數(shù)據(jù)統(tǒng)一存儲(chǔ)到 MySQL、Redis 中保證各個(gè)無(wú)狀態(tài)實(shí)例共享數(shù)據(jù),。 對(duì)于消息的接收處理我們通過(guò)純異步,、動(dòng)態(tài)多線程的方式提供了推送平臺(tái)的高性能。同時(shí)對(duì)于異步接收的消息我們通過(guò) log append 的方式保證消息先落地然后再進(jìn)行處理,,進(jìn)一步確保系統(tǒng)在異常過(guò)程中我們可以隨時(shí)恢復(fù)消息,,保證不丟失。 通過(guò)質(zhì)量保障,、全方位多維度監(jiān)控體系(基礎(chǔ)監(jiān)控,、錯(cuò)誤日志監(jiān)控、發(fā)送數(shù)據(jù)波動(dòng)監(jiān)控,、進(jìn)程監(jiān)控等監(jiān)控指標(biāo))保障系統(tǒng)在出現(xiàn)問(wèn)題時(shí)實(shí)現(xiàn)秒級(jí)報(bào)警,、及時(shí)處理保證了消息推送平臺(tái)的高穩(wěn)定性,。 本文介紹了一種基于第三方或自建推送服務(wù)、但又不強(qiáng)依賴特定推送服務(wù)的通用移動(dòng)消息推送中間件平臺(tái),,可以實(shí)現(xiàn)安全,、穩(wěn)定、可靠的消息推送功能,,并提供完善的數(shù)據(jù)統(tǒng)計(jì),,在實(shí)際應(yīng)用中,可以結(jié)合郵件,、短信,、網(wǎng)站消息,、用戶留言等打造成更加通用的企業(yè)消息平臺(tái),。 「作者寫(xiě)書(shū)的口吻完全是教條主義,感覺(jué)高高在上,?!?/p> 「作者不讓找 KOL 寫(xiě)推薦語(yǔ)?那這書(shū)還怎么賣(mài),?」 「作者個(gè)人履歷挺豐富的啊,,為什么不同意加作者介紹?十年了,,我第一次遇到這情況」 「你確定這作者在 InfoQ 上受歡迎,?」 這是一位出版社金牌編輯的原話,他所說(shuō)的就是 InfoQ 出品的第一本書(shū)——《聊聊架構(gòu)》,。社區(qū)中并不缺少架構(gòu)圖,,而是缺少架構(gòu)相關(guān)的基礎(chǔ)知識(shí)。我們花了近兩年的時(shí)間,,打磨這本可能注定無(wú)法暢銷(xiāo)的技術(shù)書(shū),。不為別的,只希望為這個(gè)行業(yè)貢獻(xiàn)一點(diǎn)點(diǎn)力量,,能夠引起一些思考也是好的,,如果能夠幫助一些軟件工程師們獲得更好的工作效率和工作品質(zhì),就超出期望值了,。
|
|
來(lái)自: tiger爸爸f54s6 > 《文件夾1》