Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 499, comments - 13, trackbacks - 0, articles - 1

          1、點(diǎn)評(píng)

          本文主要分享的是如何從零設(shè)計(jì)開(kāi)發(fā)一個(gè)中大型推送系統(tǒng),因限于篇幅,文中有些鍵技術(shù)只能一筆帶過(guò),建議有這方面興趣的讀者可以深入研究相關(guān)知識(shí)點(diǎn),從而形成橫向知識(shí)體系。

          本文適合有一定開(kāi)發(fā)、架構(gòu)經(jīng)驗(yàn)的后端程序員閱讀,文內(nèi)個(gè)別技術(shù)點(diǎn)可能并非最佳實(shí)踐,但至少都是生動(dòng)的實(shí)踐分享,至少能起到拋磚引玉的作用。希望即時(shí)通訊網(wǎng)本次整理的文章能給予你一些啟發(fā)。

          學(xué)習(xí)交流:

          - 即時(shí)通訊/推送技術(shù)開(kāi)發(fā)交流4群:101279154 [推薦]

          - 移動(dòng)端IM開(kāi)發(fā)入門文章:《新手入門一篇就夠:從零開(kāi)發(fā)移動(dòng)端IM

          (本文同步發(fā)布于:http://www.52im.net/thread-2096-1-1.html

          2、引言

          先簡(jiǎn)單介紹下本次要分享的主題,由于我最近做的是物聯(lián)網(wǎng)相關(guān)的開(kāi)發(fā)工作,其中就不免會(huì)遇到和設(shè)備的數(shù)據(jù)通信(交互)。其中最主要的工作就是要有一個(gè)系統(tǒng)來(lái)支持設(shè)備的接入、向設(shè)備推送消息,同時(shí)還得滿足大量設(shè)備接入的需求。

          正好借本文,總結(jié)和沉淀一下近期的技術(shù)心得。

          所以本次分享的內(nèi)容不但可以滿足物聯(lián)網(wǎng)領(lǐng)域同時(shí)還支持以下場(chǎng)景:

          1)基于 WEB 的聊天系統(tǒng)(點(diǎn)對(duì)點(diǎn)、群聊);

          2)WEB 應(yīng)用中需求服務(wù)端推送的場(chǎng)景;

          3)基于 SDK 的消息推送平臺(tái)。

          3、關(guān)于作者

          crossoverJie(陳杰): 90后,畢業(yè)于重慶信息工程學(xué)院,現(xiàn)供職于重慶豬八戒網(wǎng)絡(luò)有限公司。

          作者的博客:https://crossoverjie.top

          作者的Github:https://github.com/crossoverJie

          4、技術(shù)選型

          要滿足大量的連接數(shù)、同時(shí)支持雙全工通信,并且性能也得有保障。在 Java 技術(shù)棧中進(jìn)行選型首先自然是排除掉了傳統(tǒng) IO。

          那就只有選 NIO 了,在這個(gè)層面其實(shí)選擇也不多,考慮到社區(qū)、資料維護(hù)等方面最終選擇了 Netty。

          Netty源碼在線閱讀:

          Netty-4.1.x地址是:http://docs.52im.net/extend/docs/src/netty4_1/

          Netty-4.0.x地址是:http://docs.52im.net/extend/docs/src/netty4/

          Netty-3.x地址是:http://docs.52im.net/extend/docs/src/netty3/

          Netty在線API文檔:

          Netty-4.1.x API文檔(在線版):http://docs.52im.net/extend/docs/api/netty4_1/

          Netty-4.0.x API文檔(在線版):http://docs.52im.net/extend/docs/api/netty4/

          Netty-3.x API文檔(在線版):http://docs.52im.net/extend/docs/api/netty3/

          有關(guān)Netty的其它精華文章:

          有關(guān)“為何選擇Netty”的11個(gè)疑問(wèn)及解答

          開(kāi)源NIO框架八卦——到底是先有MINA還是先有Netty?

          選Netty還是Mina:深入研究與對(duì)比(一)

          選Netty還是Mina:深入研究與對(duì)比(二)

          Netty 4.x學(xué)習(xí)(一):ByteBuf詳解

          Netty 4.x學(xué)習(xí)(二):Channel和Pipeline詳解

          Netty 4.x學(xué)習(xí)(三):線程模型詳解

          實(shí)踐總結(jié):Netty3.x升級(jí)Netty4.x遇到的那些坑(線程篇)

          實(shí)踐總結(jié):Netty3.x VS Netty4.x的線程模型

          詳解Netty的安全性:原理介紹、代碼演示(上篇)

          詳解Netty的安全性:原理介紹、代碼演示(下篇)

          詳解Netty的優(yōu)雅退出機(jī)制和原理

          NIO框架詳解:Netty的高性能之道

          Twitter:如何使用Netty 4來(lái)減少JVM的GC開(kāi)銷(譯文)

          絕對(duì)干貨:基于Netty實(shí)現(xiàn)海量接入的推送服務(wù)技術(shù)要點(diǎn)

          Netty干貨分享:京東京麥的生產(chǎn)級(jí)TCP網(wǎng)關(guān)技術(shù)實(shí)踐總結(jié)

          最終的架構(gòu)圖如下:

          現(xiàn)在看著蒙沒(méi)關(guān)系,下文一一介紹。

          5、協(xié)議解析

          既然是一個(gè)消息系統(tǒng),那自然得和客戶端定義好雙方的協(xié)議格式。

          常見(jiàn)和簡(jiǎn)單的是 HTTP 協(xié)議,但我們的需求中有一項(xiàng)需要是雙全工的交互方式,同時(shí) HTTP 更多的是服務(wù)于瀏覽器。我們需要的是一個(gè)更加精簡(jiǎn)的協(xié)議,減少許多不必要的數(shù)據(jù)傳輸。

          因此我覺(jué)得最好是在滿足業(yè)務(wù)需求的情況下定制自己的私有協(xié)議,在這個(gè)場(chǎng)景下有標(biāo)準(zhǔn)的物聯(lián)網(wǎng)協(xié)議。

          如果是其他場(chǎng)景可以借鑒現(xiàn)在流行的 RPC 框架定制私有協(xié)議,使得雙方通信更加高效。

          不過(guò)根據(jù)這段時(shí)間的經(jīng)驗(yàn)來(lái)看,不管是哪種方式都得在協(xié)議中預(yù)留安全相關(guān)的位置。協(xié)議相關(guān)的內(nèi)容就不過(guò)多討論了,更多介紹具體的應(yīng)用。

          有關(guān)通信協(xié)議、協(xié)議格式的選擇,可以閱讀以下文章:

          Protobuf通信協(xié)議詳解:代碼演示、詳細(xì)原理介紹等

          一個(gè)基于Protocol Buffer的Java代碼演示

          簡(jiǎn)述傳輸層協(xié)議TCP和UDP的區(qū)別

          為什么QQ用的是UDP協(xié)議而不是TCP協(xié)議?

          移動(dòng)端即時(shí)通訊協(xié)議選擇:UDP還是TCP?

          如何選擇即時(shí)通訊應(yīng)用的數(shù)據(jù)傳輸格式

          強(qiáng)列建議將Protobuf作為你的即時(shí)通訊應(yīng)用數(shù)據(jù)傳輸格式

          全方位評(píng)測(cè):Protobuf性能到底有沒(méi)有比JSON快5倍?

          移動(dòng)端IM開(kāi)發(fā)需要面對(duì)的技術(shù)問(wèn)題(含通信協(xié)議選擇)

          簡(jiǎn)述移動(dòng)端IM開(kāi)發(fā)的那些坑:架構(gòu)設(shè)計(jì)、通信協(xié)議和客戶端

          理論聯(lián)系實(shí)際:一套典型的IM通信協(xié)議設(shè)計(jì)詳解

          58到家實(shí)時(shí)消息系統(tǒng)的協(xié)議設(shè)計(jì)等技術(shù)實(shí)踐分享

          詳解如何在NodeJS中使用Google的Protobuf

          技術(shù)掃盲:新一代基于UDP的低延時(shí)網(wǎng)絡(luò)傳輸層協(xié)議——QUIC詳解

          金蝶隨手記團(tuán)隊(duì)分享:還在用JSON? Protobuf讓數(shù)據(jù)傳輸更省更快(原理篇)

          金蝶隨手記團(tuán)隊(duì)分享:還在用JSON? Protobuf讓數(shù)據(jù)傳輸更省更快(實(shí)戰(zhàn)篇)

          >> 更多同類文章 ……

          6、簡(jiǎn)單實(shí)現(xiàn)

          首先考慮如何實(shí)現(xiàn)功能,再來(lái)思考百萬(wàn)連接的情況。

          6.1 注冊(cè)鑒權(quán)

          在做真正的消息上、下行之前首先要考慮的就是鑒權(quán)問(wèn)題。就像你使用微信一樣,第一步怎么也得是登錄吧,不能無(wú)論是誰(shuí)都可以直接連接到平臺(tái)。所以第一步得是注冊(cè)才行。

          如上面第4節(jié)架構(gòu)圖中的注冊(cè)/鑒權(quán)模塊。通常來(lái)說(shuō)都需要客戶端通過(guò) HTTP 請(qǐng)求傳遞一個(gè)唯一標(biāo)識(shí),后臺(tái)鑒權(quán)通過(guò)之后會(huì)響應(yīng)一個(gè) Token,并將這個(gè) Token 和客戶端的關(guān)系維護(hù)到 Redis 或者是 DB 中。

          客戶端將這個(gè) Token 也保存到本地,今后的每一次請(qǐng)求都得帶上這個(gè) Token。一旦這個(gè) Token 過(guò)期,客戶端需要再次請(qǐng)求獲取 Token。

          鑒權(quán)通過(guò)之后客戶端會(huì)直接通過(guò) TCP 長(zhǎng)連接到圖中的 push-server 模塊。這個(gè)模塊就是真正處理消息的上、下行。

          6.2 保存通道關(guān)系

          在連接接入之后,真正處理業(yè)務(wù)之前需要將當(dāng)前的客戶端和 Channel 的關(guān)系維護(hù)起來(lái)。

          假設(shè)客戶端的唯一標(biāo)識(shí)是手機(jī)號(hào)碼,那就需要把手機(jī)號(hào)碼和當(dāng)前的 Channel 維護(hù)到一個(gè) Map 中。

          這點(diǎn)和之前 Spring Boot 整合長(zhǎng)連接心跳機(jī)制類似,如下圖:

          同時(shí)為了可以通過(guò) Channel 獲取到客戶端唯一標(biāo)識(shí)(手機(jī)號(hào)碼),還需要在 Channel 中設(shè)置對(duì)應(yīng)的屬性:

          publicstaticvoidputClientId(Channel channel, String clientId) {

              channel.attr(CLIENT_ID).set(clientId);

          }

          獲取手機(jī)號(hào)碼時(shí):

          publicstaticString getClientId(Channel channel) {

              return(String)getAttribute(channel, CLIENT_ID);

          }

          這樣當(dāng)我們客戶端下線時(shí)便可以記錄相關(guān)日志:

          String telNo = NettyAttrUtil.getClientId(ctx.channel());

          NettySocketHolder.remove(telNo);

          log.info("客戶端下線,TelNo="+  telNo);

          這里有一點(diǎn)需要注意:存放客戶端與 Channel 關(guān)系的 Map 最好是預(yù)設(shè)好大小(避免經(jīng)常擴(kuò)容),因?yàn)樗鼘⑹鞘褂米顬轭l繁同時(shí)也是占用內(nèi)存最大的一個(gè)對(duì)象。

          6.3 消息上行

          接下來(lái)則是真正的業(yè)務(wù)數(shù)據(jù)上傳,通常來(lái)說(shuō)第一步是需要判斷上傳消息輸入什么業(yè)務(wù)類型。在聊天場(chǎng)景中,有可能上傳的是文本、圖片、視頻等內(nèi)容。

          所以我們得進(jìn)行區(qū)分,來(lái)做不同的處理,這就和客戶端協(xié)商的協(xié)議有關(guān)了:

          1)可以利用消息頭中的某個(gè)字段進(jìn)行區(qū)分;

          2)更簡(jiǎn)單的就是一個(gè) JSON 消息,拿出一個(gè)字段用于區(qū)分不同消息。

          不管是哪種只要可以區(qū)分出來(lái)即可。

          6.4 消息解析與業(yè)務(wù)解耦

          消息可以解析之后便是處理業(yè)務(wù),比如可以是寫(xiě)入數(shù)據(jù)庫(kù)、調(diào)用其他接口等。

          我們都知道在 Netty 中處理消息一般是在 channelRead() 方法中: 

          在這里可以解析消息,區(qū)分類型。但如果我們的業(yè)務(wù)邏輯也寫(xiě)在里面,那這里的內(nèi)容將是巨多無(wú)比。

          甚至我們分為好幾個(gè)開(kāi)發(fā)來(lái)處理不同的業(yè)務(wù),這樣將會(huì)出現(xiàn)許多沖突、難以維護(hù)等問(wèn)題。所以非常有必要將消息解析與業(yè)務(wù)處理完全分離開(kāi)來(lái)。

          這時(shí)面向接口編程就發(fā)揮作用了。這里的核心代碼和 「造個(gè)輪子」——cicada(輕量級(jí) Web 框架)是一致的(另外,即時(shí)通訊網(wǎng)的MobileIMSDK工程也使用了同樣的API解偶設(shè)計(jì)思路)。

          都是先定義一個(gè)接口用于處理業(yè)務(wù)邏輯,然后在解析消息之后通過(guò)反射創(chuàng)建具體的對(duì)象執(zhí)行其中的處理函數(shù)即可。

          這樣不同的業(yè)務(wù)、不同的開(kāi)發(fā)人員只需要實(shí)現(xiàn)這個(gè)接口同時(shí)實(shí)現(xiàn)自己的業(yè)務(wù)邏輯即可。

          偽代碼如下:


          想要了解 cicada 的具體實(shí)現(xiàn)請(qǐng)點(diǎn)擊這里:

          https://github.com/TogetherOS/cicada

          上行還有一點(diǎn)需要注意:由于是基于長(zhǎng)連接,所以客戶端需要定期發(fā)送心跳包用于維護(hù)本次連接。

          同時(shí)服務(wù)端也會(huì)有相應(yīng)的檢查,N 個(gè)時(shí)間間隔沒(méi)有收到消息之后,將會(huì)主動(dòng)斷開(kāi)連接節(jié)省資源。

          這點(diǎn)使用一個(gè) IdleStateHandler 就可實(shí)現(xiàn)。

          6.5 消息下行

          有了上行自然也有下行。比如在聊天的場(chǎng)景中,有兩個(gè)客戶端連上了 push-server,它們直接需要點(diǎn)對(duì)點(diǎn)通信。

          這時(shí)的流程是:

          1)A 將消息發(fā)送給服務(wù)器;

          2)服務(wù)器收到消息之后,得知消息是要發(fā)送給 B,需要在內(nèi)存中找到 B 的 Channel;

          3)通過(guò) B 的 Channel 將 A 的消息轉(zhuǎn)發(fā)下去。

          這就是一個(gè)下行的流程。甚至管理員需要給所有在線用戶發(fā)送系統(tǒng)通知也是類似:遍歷保存通道關(guān)系的 Map,挨個(gè)發(fā)送消息即可。這也是之前需要存放到 Map 中的主要原因。

          偽代碼如下:

          具體可以參考:

          https://github.com/crossoverJie/netty-action/

          7、分布式方案

          單機(jī)版的實(shí)現(xiàn)了,現(xiàn)在著重講講如何實(shí)現(xiàn)百萬(wàn)連接。

          百萬(wàn)連接其實(shí)只是一個(gè)形容詞,更多的是想表達(dá)如何來(lái)實(shí)現(xiàn)一個(gè)分布式的方案,可以靈活的水平拓展從而能支持更多的連接。在做這個(gè)事前,首先得搞清楚我們單機(jī)版的能支持多少連接。

          影響這個(gè)的因素就比較多了:

          1)服務(wù)器自身配置:內(nèi)存、CPU、網(wǎng)卡、Linux 支持的最大文件打開(kāi)數(shù)等;

          2)應(yīng)用自身配置:因?yàn)?Netty 本身需要依賴于堆外內(nèi)存,但是 JVM 本身也是需要占用一部分內(nèi)存的,比如存放通道關(guān)系的大 Map。這點(diǎn)需要結(jié)合自身情況進(jìn)行調(diào)整。

          結(jié)合以上的情況可以測(cè)試出單個(gè)節(jié)點(diǎn)能支持的最大連接數(shù)。單機(jī)無(wú)論怎么優(yōu)化都是有上限的,這也是分布式主要解決的問(wèn)題。

          7.1 架構(gòu)介紹

          在講具體實(shí)現(xiàn)之前首先得講講上文貼出的整體架構(gòu)圖:

          先從左邊開(kāi)始。上文提到的注冊(cè)鑒權(quán)模塊也是集群部署的,通過(guò)前置的 Nginx 進(jìn)行負(fù)載。之前也提過(guò)了它主要的目的是來(lái)做鑒權(quán)并返回一個(gè) Token 給客戶端。

          但是 push-server 集群之后它又多了一個(gè)作用。那就是得返回一臺(tái)可供當(dāng)前客戶端使用的 push-server。

          右側(cè)的平臺(tái)一般指管理平臺(tái),它可以查看當(dāng)前的實(shí)時(shí)在線數(shù)、給指定客戶端推送消息等。推送消息則需要經(jīng)過(guò)一個(gè)推送路由(push-server)找到真正的推送節(jié)點(diǎn)。

          其余的中間件如:Redis、ZooKeeper、Kafka、MySQL 都是為了這些功能所準(zhǔn)備的,具體看下面的實(shí)現(xiàn)。

          7.2 注冊(cè)發(fā)現(xiàn)

          首先第一個(gè)問(wèn)題則是 注冊(cè)發(fā)現(xiàn),push-server 變?yōu)槎嗯_(tái)之后如何給客戶端選擇一臺(tái)可用的節(jié)點(diǎn)是第一個(gè)需要解決的。

          這塊的內(nèi)容其實(shí)已經(jīng)在 分布式(一) 搞定服務(wù)注冊(cè)與發(fā)現(xiàn)中詳細(xì)講過(guò)了。所有的 push-server 在啟動(dòng)時(shí)候需要將自身的信息注冊(cè)到 ZooKeeper 中。

          注冊(cè)鑒權(quán)模塊會(huì)訂閱 ZooKeeper 中的節(jié)點(diǎn),從而可以獲取最新的服務(wù)列表,結(jié)構(gòu)如下:

          以下是一些偽代碼:應(yīng)用啟動(dòng)注冊(cè) ZooKeeper

          對(duì)于注冊(cè)鑒權(quán)模塊來(lái)說(shuō)只需要訂閱這個(gè) ZooKeeper 節(jié)點(diǎn):

          7.3 路由策略

          既然能獲取到所有的服務(wù)列表,那如何選擇一臺(tái)剛好合適的 push-server 給客戶端使用呢?

          這個(gè)過(guò)程重點(diǎn)要考慮以下幾點(diǎn):

          1)盡量保證各個(gè)節(jié)點(diǎn)的連接均勻;

          2)增刪節(jié)點(diǎn)是否要做 Rebalance。

          首先保證均衡有以下幾種算法:

          1)輪詢:挨個(gè)將各個(gè)節(jié)點(diǎn)分配給客戶端。但會(huì)出現(xiàn)新增節(jié)點(diǎn)分配不均勻的情況;

          2)Hash 取模的方式:類似于 HashMap,但也會(huì)出現(xiàn)輪詢的問(wèn)題。當(dāng)然也可以像 HashMap 那樣做一次 Rebalance,讓所有的客戶端重新連接。不過(guò)這樣會(huì)導(dǎo)致所有的連接出現(xiàn)中斷重連,代價(jià)有點(diǎn)大。由于 Hash 取模方式的問(wèn)題帶來(lái)了一致性 Hash 算法,但依然會(huì)有一部分的客戶端需要 Rebalance;

          3)權(quán)重:可以手動(dòng)調(diào)整各個(gè)節(jié)點(diǎn)的負(fù)載情況,甚至可以做成自動(dòng)的,基于監(jiān)控當(dāng)某些節(jié)點(diǎn)負(fù)載較高就自動(dòng)調(diào)低權(quán)重,負(fù)載較低的可以提高權(quán)重。

          還有一個(gè)問(wèn)題是:當(dāng)我們?cè)谥貑⒉糠謶?yīng)用進(jìn)行升級(jí)時(shí),在該節(jié)點(diǎn)上的客戶端怎么處理?

          由于我們有心跳機(jī)制,當(dāng)心跳不通之后就可以認(rèn)為該節(jié)點(diǎn)出現(xiàn)問(wèn)題了。那就得重新請(qǐng)求注冊(cè)鑒權(quán)模塊獲取一個(gè)可用的節(jié)點(diǎn)。在弱網(wǎng)情況下同樣適用。

          如果這時(shí)客戶端正在發(fā)送消息,則需要將消息保存到本地等待獲取到新的節(jié)點(diǎn)之后再次發(fā)送。

          7.4 有狀態(tài)連接

          在這樣的場(chǎng)景中不像是 HTTP 那樣是無(wú)狀態(tài)的,我們得明確的知道各個(gè)客戶端和連接的關(guān)系。

          在上文的單機(jī)版中我們將這個(gè)關(guān)系保存到本地的緩存中,但在分布式環(huán)境中顯然行不通了。

          比如在平臺(tái)向客戶端推送消息的時(shí)候,它得首先知道這個(gè)客戶端的通道保存在哪臺(tái)節(jié)點(diǎn)上。

          借助我們以前的經(jīng)驗(yàn),這樣的問(wèn)題自然得引入一個(gè)第三方中間件用來(lái)存放這個(gè)關(guān)系。

          也就是架構(gòu)圖中的存放路由關(guān)系的 Redis,在客戶端接入 push-server 時(shí)需要將當(dāng)前客戶端唯一標(biāo)識(shí)和服務(wù)節(jié)點(diǎn)的 ip+port 存進(jìn) Redis。

          同時(shí)在客戶端下線時(shí)候得在 Redis 中刪掉這個(gè)連接關(guān)系。這樣在理想情況下各個(gè)節(jié)點(diǎn)內(nèi)存中的 Map 關(guān)系加起來(lái)應(yīng)該正好等于 Redis 中的數(shù)據(jù)。

          偽代碼如下:

          這里存放路由關(guān)系的時(shí)候會(huì)有并發(fā)問(wèn)題,最好是換為一個(gè) Lua 腳本。

          7.5 推送路由

          設(shè)想這樣一個(gè)場(chǎng)景:管理員需要給最近注冊(cè)的客戶端推送一個(gè)系統(tǒng)消息會(huì)怎么做?

          結(jié)合架構(gòu)圖,假設(shè)這批客戶端有 10W 個(gè),首先我們需要將這批號(hào)碼通過(guò)平臺(tái)下的 Nginx 下發(fā)到一個(gè)推送路由中。

          為了提高效率甚至可以將這批號(hào)碼再次分散到每個(gè) push-route 中。拿到具體號(hào)碼之后再根據(jù)號(hào)碼的數(shù)量啟動(dòng)多線程的方式去之前的路由 Redis 中獲取客戶端所對(duì)應(yīng)的 push-server。

          再通過(guò) HTTP 的方式調(diào)用 push-server 進(jìn)行真正的消息下發(fā)(Netty 也很好的支持 HTTP 協(xié)議)。

          推送成功之后需要將結(jié)果更新到數(shù)據(jù)庫(kù)中,不在線的客戶端可以根據(jù)業(yè)務(wù)再次推送等。

          7.6 消息流轉(zhuǎn)

          也許有些場(chǎng)景對(duì)于客戶端上行的消息非常看重,需要做持久化,并且消息量非常大。

          在 push-sever 做業(yè)務(wù)顯然不合適,這時(shí)完全可以選擇 Kafka 來(lái)解耦。將所有上行的數(shù)據(jù)直接往 Kafka 里丟后就不管了。再由消費(fèi)程序?qū)?shù)據(jù)取出寫(xiě)入數(shù)據(jù)庫(kù)中即可。

          8、分布式帶來(lái)的問(wèn)題

          分布式解決了性能問(wèn)題但卻帶來(lái)了其他麻煩。

          8.1 應(yīng)用監(jiān)控

          比如如何知道線上幾十個(gè) push-server 節(jié)點(diǎn)的健康狀況?這時(shí)就得監(jiān)控系統(tǒng)發(fā)揮作用了,我們需要知道各個(gè)節(jié)點(diǎn)當(dāng)前的內(nèi)存使用情況、GC。

          以及操作系統(tǒng)本身的內(nèi)存使用,畢竟 Netty 大量使用了堆外內(nèi)存。同時(shí)需要監(jiān)控各個(gè)節(jié)點(diǎn)當(dāng)前的在線數(shù),以及 Redis 中的在線數(shù)。理論上這兩個(gè)數(shù)應(yīng)該是相等的。

          這樣也可以知道系統(tǒng)的使用情況,可以靈活的維護(hù)這些節(jié)點(diǎn)數(shù)量。

          8.2 日志處理

          日志記錄也變得異常重要了,比如哪天反饋有個(gè)客戶端一直連不上,你得知道問(wèn)題出在哪里。

          最好是給每次請(qǐng)求都加上一個(gè) traceID 記錄日志,這樣就可以通過(guò)這個(gè)日志在各個(gè)節(jié)點(diǎn)中查看到底是卡在了哪里。以及 ELK 這些工具都得用起來(lái)才行。

          9、本文小結(jié)

          本次是結(jié)合我日常經(jīng)驗(yàn)得出的,有些坑可能在工作中并沒(méi)有踩到,所以還會(huì)有一些遺漏的地方。

          就目前來(lái)看想做一個(gè)穩(wěn)定的推送系統(tǒng)是比較麻煩的,其中涉及到的點(diǎn)非常多,只有真正做過(guò)之后才會(huì)知道。

          附錄:更多推送技術(shù)相關(guān)文章

          iOS的推送服務(wù)APNs詳解:設(shè)計(jì)思路、技術(shù)原理及缺陷等

          信鴿團(tuán)隊(duì)原創(chuàng):一起走過(guò) iOS10 上消息推送(APNS)的坑

          Android端消息推送總結(jié):實(shí)現(xiàn)原理、心跳保活、遇到的問(wèn)題等

          掃盲貼:認(rèn)識(shí)MQTT通信協(xié)議

          一個(gè)基于MQTT通信協(xié)議的完整Android推送Demo

          IBM技術(shù)經(jīng)理訪談:MQTT協(xié)議的制定歷程、發(fā)展現(xiàn)狀等

          求教android消息推送:GCM、XMPP、MQTT三種方案的優(yōu)劣

          移動(dòng)端實(shí)時(shí)消息推送技術(shù)淺析

          掃盲貼:淺談iOS和Android后臺(tái)實(shí)時(shí)消息推送的原理和區(qū)別

          絕對(duì)干貨:基于Netty實(shí)現(xiàn)海量接入的推送服務(wù)技術(shù)要點(diǎn)

          移動(dòng)端IM實(shí)踐:谷歌消息推送服務(wù)(GCM)研究(來(lái)自微信)

          為何微信、QQ這樣的IM工具不使用GCM服務(wù)推送消息?

          極光推送系統(tǒng)大規(guī)模高并發(fā)架構(gòu)的技術(shù)實(shí)踐分享

          從HTTP到MQTT:一個(gè)基于位置服務(wù)的APP數(shù)據(jù)通信實(shí)踐概述

          魅族2500萬(wàn)長(zhǎng)連接的實(shí)時(shí)消息推送架構(gòu)的技術(shù)實(shí)踐分享

          專訪魅族架構(gòu)師:海量長(zhǎng)連接的實(shí)時(shí)消息推送系統(tǒng)的心得體會(huì)

          深入的聊聊Android消息推送這件小事

          基于WebSocket實(shí)現(xiàn)Hybrid移動(dòng)應(yīng)用的消息推送實(shí)踐(含代碼示例)

          一個(gè)基于長(zhǎng)連接的安全可擴(kuò)展的訂閱/推送服務(wù)實(shí)現(xiàn)思路

          實(shí)踐分享:如何構(gòu)建一套高可用的移動(dòng)端消息推送系統(tǒng)?

          Go語(yǔ)言構(gòu)建千萬(wàn)級(jí)在線的高并發(fā)消息推送系統(tǒng)實(shí)踐(來(lái)自360公司)

          騰訊信鴿技術(shù)分享:百億級(jí)實(shí)時(shí)消息推送的實(shí)戰(zhàn)經(jīng)驗(yàn)

          百萬(wàn)在線的美拍直播彈幕系統(tǒng)的實(shí)時(shí)推送技術(shù)實(shí)踐之路

          京東京麥商家開(kāi)放平臺(tái)的消息推送架構(gòu)演進(jìn)之路

          了解iOS消息推送一文就夠:史上最全iOS Push技術(shù)詳解

          基于APNs最新HTTP/2接口實(shí)現(xiàn)iOS的高性能消息推送(服務(wù)端篇)

          解密“達(dá)達(dá)-京東到家”的訂單即時(shí)派發(fā)技術(shù)原理和實(shí)踐

          技術(shù)干貨:從零開(kāi)始,教你設(shè)計(jì)一個(gè)百萬(wàn)級(jí)的消息推送系統(tǒng)

          >> 更多同類文章 ……

          (本文同步發(fā)布于:http://www.52im.net/thread-2096-1-1.html



          作者:Jack Jiang (點(diǎn)擊作者姓名進(jìn)入Github)
          出處:http://www.52im.net/space-uid-1.html
          交流:歡迎加入即時(shí)通訊開(kāi)發(fā)交流群 215891622
          討論:http://www.52im.net/
          Jack Jiang同時(shí)是【原創(chuàng)Java Swing外觀工程BeautyEye】【輕量級(jí)移動(dòng)端即時(shí)通訊框架MobileIMSDK】的作者,可前往下載交流。
          本博文 歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處(也可前往 我的52im.net 找到我)。


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 石泉县| 阜南县| 桑植县| 鸡泽县| 曲松县| 罗源县| 焉耆| 增城市| 深泽县| 黎城县| 绥中县| 罗源县| 玉山县| 滕州市| 阿巴嘎旗| 姜堰市| 衢州市| 锡林郭勒盟| 措美县| 惠安县| 泰来县| 米脂县| 成安县| 上思县| 安吉县| 阿拉尔市| 四川省| 睢宁县| 炎陵县| 太湖县| 垫江县| 承德市| 茌平县| 荣昌县| 巴楚县| 大洼县| 枞阳县| 成武县| 巨野县| 沛县| 无为县|