Jack Jiang

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

               摘要: 一、引言對(duì)于后端程序員來(lái)說(shuō),127.0.0.1和0.0.0.0這兩個(gè)IP地址再熟悉不過(guò)了,看起來(lái)好像就那么回事,但真正較起真來(lái),這兩個(gè)IP地址到底有什么作用以及到底有什么不同?貌似誰(shuí)可以輕松回答,但張嘴卻又不知從何說(shuō)起。。。(這要是面視,估計(jì)真會(huì)被這搞砸...)本文將系統(tǒng)地總結(jié)127.0.0.1和0.0.0.0這兩個(gè)IP地址的作用,以及它們之間的區(qū)別,希望能為你解惑。  * 推...  閱讀全文

          posted @ 2020-03-03 15:53 Jack Jiang 閱讀(681) | 評(píng)論 (0)編輯 收藏

               摘要: 1、引言上個(gè)月在知乎上發(fā)表的由“袁輝輝”分享的關(guān)于TIM進(jìn)程永生方面的文章(即時(shí)通訊網(wǎng)重新整理后的標(biāo)題是:《史上最強(qiáng)Android保活思路:深入剖析騰訊TIM的進(jìn)程永生技術(shù)》),短時(shí)間內(nèi)受到大量關(guān)注,可惜在短短的幾十個(gè)小時(shí)后,就在一股神秘力量的干預(yù)下被強(qiáng)行刪除了。。。 ▲ 該文在知乎上從發(fā)布到刪除的時(shí)間歷程(中間省略了N條讀者的評(píng)論)在《史上最強(qiáng)And...  閱讀全文

          posted @ 2020-02-26 22:44 Jack Jiang 閱讀(481) | 評(píng)論 (0)編輯 收藏

          本文由馬蜂窩技術(shù)團(tuán)隊(duì)電商交易基礎(chǔ)平臺(tái)研發(fā)工程師"Anti Walker"原創(chuàng)分享。

          一、引言

          即時(shí)通訊(IM)功能對(duì)于電商平臺(tái)來(lái)說(shuō)非常重要,特別是旅游電商。

          從商品復(fù)雜性來(lái)看,一個(gè)旅游商品可能會(huì)包括用戶(hù)在未來(lái)一段時(shí)間的衣、食、住、行等方方面面。從消費(fèi)金額來(lái)看,往往單次消費(fèi)額度較大。對(duì)目的地的陌生、在行程中可能的問(wèn)題,這些因素使用戶(hù)在購(gòu)買(mǎi)前、中、后都存在和商家溝通的強(qiáng)烈需求。可以說(shuō),一個(gè)好用的 IM 可以在一定程度上對(duì)企業(yè)電商業(yè)務(wù)的 GMV 起到促進(jìn)作用。

          本文我們將結(jié)合馬蜂窩旅游電商IM系統(tǒng)的發(fā)展歷程,單獨(dú)介紹基于Go重構(gòu)分布式IM系統(tǒng)過(guò)程中的實(shí)踐和總結(jié)(本文相當(dāng)于《從游擊隊(duì)到正規(guī)軍(一):馬蜂窩旅游網(wǎng)的IM系統(tǒng)架構(gòu)演進(jìn)之路》一文的進(jìn)階篇),希望可以給有相似問(wèn)題的朋友一些借鑒。

          另外:如果你對(duì)Go在高并發(fā)系統(tǒng)中的應(yīng)用感興趣,即時(shí)通訊網(wǎng)的以下兩篇也值得一讀:

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

          12306搶票帶來(lái)的啟示:看我如何用Go實(shí)現(xiàn)百萬(wàn)QPS的秒殺系統(tǒng)(含源碼)

          系列文章:

          從游擊隊(duì)到正規(guī)軍(一):馬蜂窩旅游網(wǎng)的IM系統(tǒng)架構(gòu)演進(jìn)之路

          從游擊隊(duì)到正規(guī)軍(二):馬蜂窩旅游網(wǎng)的IM客戶(hù)端架構(gòu)演進(jìn)和實(shí)踐總結(jié)

          從游擊隊(duì)到正規(guī)軍(三):基于Go的馬蜂窩旅游網(wǎng)分布式IM系統(tǒng)技術(shù)實(shí)踐》(* 本文

          關(guān)于馬蜂窩旅游網(wǎng): 

          馬蜂窩旅游網(wǎng)是中國(guó)領(lǐng)先的自由行服務(wù)平臺(tái),由陳罡和呂剛創(chuàng)立于2006年,從2010年正式開(kāi)始公司化運(yùn)營(yíng)。馬蜂窩的景點(diǎn)、餐飲、酒店等點(diǎn)評(píng)信息均來(lái)自上億用戶(hù)的真實(shí)分享,每年幫助過(guò)億的旅行者制定自由行方案。

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

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

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

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

          二、相關(guān)文章

          一套海量在線用戶(hù)的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)

          一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案

          從零到卓越:京東客服即時(shí)通訊系統(tǒng)的技術(shù)架構(gòu)演進(jìn)歷程

          蘑菇街即時(shí)通訊/IM服務(wù)器開(kāi)發(fā)之架構(gòu)選擇

          以微博類(lèi)應(yīng)用場(chǎng)景為例,總結(jié)海量社交系統(tǒng)的架構(gòu)設(shè)計(jì)步驟

          一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐

          騰訊QQ1.4億在線用戶(hù)的技術(shù)挑戰(zhàn)和架構(gòu)演進(jìn)之路PPT

          微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡(jiǎn)(演講全文)

          如何解讀《微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡(jiǎn)》

          快速裂變:見(jiàn)證微信強(qiáng)大后臺(tái)架構(gòu)從0到1的演進(jìn)歷程(一)

          瓜子IM智能客服系統(tǒng)的數(shù)據(jù)架構(gòu)設(shè)計(jì)(整理自現(xiàn)場(chǎng)演講,有配套PPT)

          阿里釘釘技術(shù)分享:企業(yè)級(jí)IM王者——釘釘在后端架構(gòu)上的過(guò)人之處

          三、技術(shù)背景和問(wèn)題

          與廣義上的即時(shí)通訊不同,電商各業(yè)務(wù)線有其特有業(yè)務(wù)邏輯,如客服聊天系統(tǒng)的客人分配邏輯、敏感詞檢測(cè)邏輯等,這些往往要耦合進(jìn)通信流程中。隨著接入業(yè)務(wù)線越來(lái)越多,即時(shí)通訊服務(wù)冗余度會(huì)越來(lái)越高。同時(shí)整個(gè)消息鏈路追溯復(fù)雜,服務(wù)穩(wěn)定性很受業(yè)務(wù)邏輯的影響。

          之前我們 IM 應(yīng)用中的消息推送主要基于輪詢(xún)技術(shù),消息輪詢(xún)模塊的長(zhǎng)連接請(qǐng)求是通過(guò) php-fpm 掛載在阻塞隊(duì)列上實(shí)現(xiàn)。當(dāng)請(qǐng)求量較大時(shí),如果不能及時(shí)釋放 php-fpm 進(jìn)程,對(duì)服務(wù)器的性能消耗很大。

          為了解決這個(gè)問(wèn)題,我們?cè)?OpenResty+Lua 的方式進(jìn)行改造,利用 Lua 協(xié)程的方式將整體的 polling 的能力從 PHP 轉(zhuǎn)交到 Lua 處理,釋放一部 PHP 的壓力。這種方式雖然能提升一部分性能,但 PHP-Lua 的混合異構(gòu)模式,使系統(tǒng)在使用、升級(jí)、調(diào)試和維護(hù)上都很麻煩,通用性也較差,很多業(yè)務(wù)場(chǎng)景下還是要依賴(lài) PHP 接口,優(yōu)化效果并不明顯。

          為了解決以上問(wèn)題,我們決定結(jié)合電商 IM 的特定背景對(duì) IM 服務(wù)進(jìn)行重構(gòu),核心是實(shí)現(xiàn)業(yè)務(wù)邏輯和即時(shí)通訊服務(wù)的分離。

          更多有關(guān)馬蜂窩旅游網(wǎng)的IM系統(tǒng)架構(gòu)的演進(jìn)過(guò)程,請(qǐng)?jiān)斪x:《從游擊隊(duì)到正規(guī)軍(一):馬蜂窩旅游網(wǎng)的IM系統(tǒng)架構(gòu)演進(jìn)之路》一文,在此不再贅述。

          四、基于Go的雙層分布式IM架構(gòu)

          4.1、實(shí)現(xiàn)目標(biāo)

          1)業(yè)務(wù)解耦:

          將業(yè)務(wù)邏輯與通信流程剝離,使 IM 服務(wù)架構(gòu)更加清晰,實(shí)現(xiàn)與電商 IM 業(yè)務(wù)邏輯的完全分離,保證服務(wù)穩(wěn)定性。

          2)接入方式靈活:

          之前新業(yè)務(wù)接入時(shí),需要在業(yè)務(wù)服務(wù)器上配置 OpenResty 環(huán)境及 Lua 協(xié)程代碼,非常不便,IM 服務(wù)的通用性也很差。考慮到現(xiàn)有業(yè)務(wù)的實(shí)際情況,我們希望 IM 系統(tǒng)可以提供 HTTP 和 WebSocket 兩種接入方式,供業(yè)務(wù)方根據(jù)不同的場(chǎng)景來(lái)靈活使用。

          比如已經(jīng)接入且運(yùn)行良好的電商定制化團(tuán)隊(duì)的待辦系統(tǒng)、定制游搶單系統(tǒng)、投訴系統(tǒng)等下行相關(guān)的系統(tǒng)等,這些業(yè)務(wù)沒(méi)有明顯的高并發(fā)需求,可以通過(guò) HTTP 方式迅速接入,不需要熟悉稍顯復(fù)雜的 WebSocket 協(xié)議,進(jìn)而降低不必要的研發(fā)成本。

          3)架構(gòu)可擴(kuò)展:

          為了應(yīng)對(duì)業(yè)務(wù)的持續(xù)增長(zhǎng)給系統(tǒng)性能帶來(lái)的挑戰(zhàn),我們考慮用分布式架構(gòu)來(lái)設(shè)計(jì)即時(shí)通訊服務(wù),使系統(tǒng)具有持續(xù)擴(kuò)展及提升的能力。

          4.2、語(yǔ)言選擇

          目前,馬蜂窩技術(shù)體系主要包括 PHP,Java,Golang,技術(shù)棧比較豐富,使業(yè)務(wù)做選型時(shí)可以根據(jù)問(wèn)題場(chǎng)景選擇更合適的工具和語(yǔ)言。

          結(jié)合 IM 具體應(yīng)用場(chǎng)景,我們選擇 Go 的原因包括:

          • 1)運(yùn)行性能:在性能上,尤其是針對(duì)網(wǎng)絡(luò)通信等 IO 密集型應(yīng)用場(chǎng)景。Go 系統(tǒng)的性能更接近 C/C++;
          • 2)開(kāi)發(fā)效率:Go 使用起來(lái)簡(jiǎn)單,代碼編寫(xiě)效率高,上手也很快,尤其是對(duì)于有一定 C++ 基礎(chǔ)的開(kāi)發(fā)者,一周就能上手寫(xiě)代碼了。

          4.3、架構(gòu)設(shè)計(jì)

          整體架構(gòu)圖如下: 

          名詞解釋?zhuān)?/strong>

          • 1)客戶(hù):一般指購(gòu)買(mǎi)商品的用戶(hù);
          • 2)商家:提供服務(wù)的供應(yīng)商,商家會(huì)有客服人員,提供給客戶(hù)一個(gè)在線咨詢(xún)的作用;
          • 3)分發(fā)模塊:即 Dispatcher,提供消息分發(fā)的給指定的工作模塊的橋接作用;
          • 4)工作模塊:即 Worker 服務(wù)器,用來(lái)提供 WebSocket 服務(wù),是真正工作的一個(gè)模塊。

          架構(gòu)分層:

          • 1)展示層:提供 HTTP 和 WebSocket 兩種接入方式;
          • 2)業(yè)務(wù)層:負(fù)責(zé)初始化消息線和業(yè)務(wù)邏輯處理。如果客戶(hù)端以 HTTP 方式接入,會(huì)以 JSON 格式把消息發(fā)送給業(yè)務(wù)服務(wù)器進(jìn)行消息解碼、客服分配、敏感詞過(guò)濾,然后下發(fā)到消息分發(fā)模塊準(zhǔn)備下一步的轉(zhuǎn)換;通過(guò) WebSocket 接入的業(yè)務(wù)則不需要消息分發(fā),直接以 WebSocket 方式發(fā)送至消息處理模塊中;
          • 3)服務(wù)層:由消息分發(fā)和消息處理這兩層組成,分別以分布式的方式部署多個(gè) Dispatcher 和 Worker 節(jié)點(diǎn)。Dispatcher 負(fù)責(zé)檢索出接收者所在的服務(wù)器位置,將消息以 RPC 的方式發(fā)送到合適的 Worker 上,再由消息處理模塊通過(guò) WebSocket 把消息推送給客戶(hù)端;
          • 4)數(shù)據(jù)層:Redis 集群,記錄用戶(hù)身份、連接信息、客戶(hù)端平臺(tái)(移動(dòng)端、網(wǎng)頁(yè)端、桌面端)等組成的唯一 Key。

          4.4、服務(wù)流程

          步驟一:

          如上圖右側(cè)所示:

          用戶(hù)客戶(hù)端與消息處理模塊建立 WebSocket 長(zhǎng)連接;

          通過(guò)負(fù)載均衡算法,使客戶(hù)端連接到合適的服務(wù)器(消息處理模塊的某個(gè) Worker);

          連接成功后,記錄用戶(hù)連接信息,包括用戶(hù)角色(客人或商家)、客戶(hù)端平臺(tái)(移動(dòng)端、網(wǎng)頁(yè)端、桌面端)等組成唯一 Key,記錄到 Redis 集群。

          步驟二:

          如圖左側(cè)所示,當(dāng)購(gòu)買(mǎi)商品的用戶(hù)要給管家發(fā)消息的時(shí)候,先通過(guò) HTTP 請(qǐng)求把消息發(fā)給業(yè)務(wù)服務(wù)器,業(yè)務(wù)服務(wù)端對(duì)消息進(jìn)行業(yè)務(wù)邏輯處理。

          1)該步驟本身是一個(gè) HTTP 請(qǐng)求,所以可以接入各種不同開(kāi)發(fā)語(yǔ)言的客戶(hù)端。通過(guò) JSON 格式把消息發(fā)送給業(yè)務(wù)服務(wù)器,業(yè)務(wù)服務(wù)器先把消息解碼,然后拿到這個(gè)用戶(hù)要發(fā)送給哪個(gè)商家的客服的。

          2)如果這個(gè)購(gòu)買(mǎi)者之前沒(méi)有聊過(guò)天,則在業(yè)務(wù)服務(wù)器邏輯里需要有一個(gè)分配客服的過(guò)程,即建立購(gòu)買(mǎi)者和商家的客服之間的連接關(guān)系。拿到這個(gè)客服的 ID,用來(lái)做業(yè)務(wù)消息下發(fā);如果之前已經(jīng)聊過(guò)天,則略過(guò)此環(huán)節(jié)。

          3)在業(yè)務(wù)服務(wù)器,消息會(huì)異步入數(shù)據(jù)庫(kù)。保證消息不會(huì)丟失。

          步驟三:

          業(yè)務(wù)服務(wù)端以 HTTP 請(qǐng)求把消息發(fā)送到消息分發(fā)模塊。這里分發(fā)模塊的作用是進(jìn)行中轉(zhuǎn),最終使服務(wù)端的消息下發(fā)給指定的商家。

          步驟四:

          基于 Redis 集群中的用戶(hù)連接信息,消息分發(fā)模塊將消息轉(zhuǎn)發(fā)到目標(biāo)用戶(hù)連接的 WebSocket 服務(wù)器(消息處理模塊中的某一個(gè) Worker)

          1)分發(fā)模塊通過(guò) RPC 方式把消息轉(zhuǎn)發(fā)到目標(biāo)用戶(hù)連接的 Worker,RPC 的方式性能更快,而且傳輸?shù)臄?shù)據(jù)也少,從而節(jié)約了服務(wù)器的成本。

          2)消息透?jìng)?Worker 的時(shí)候,多種策略保障消息一定會(huì)下發(fā)到 Worker。

          步驟五:

          消息處理模塊將消息通過(guò) WebSocket 協(xié)議推送到客戶(hù)端。

          1)在投遞的時(shí)候,接收者要有一個(gè) ACK(應(yīng)答) 信息來(lái)回饋給 Worker 服務(wù)器,告訴 Worker 服務(wù)器,下發(fā)的消息接收者已經(jīng)收到了。

          2)如果接收者沒(méi)有發(fā)送這個(gè) ACK 來(lái)告訴 Worker 服務(wù)器,Worker 服務(wù)器會(huì)在一定的時(shí)間內(nèi)來(lái)重新把這個(gè)信息發(fā)送給消息接收者。

          3)如果投遞的信息已經(jīng)發(fā)送給客戶(hù)端,客戶(hù)端也收到了,但是因?yàn)榫W(wǎng)絡(luò)抖動(dòng),沒(méi)有把 ACK 信息發(fā)送給服務(wù)器,那服務(wù)器會(huì)重復(fù)投遞給客戶(hù)端,這時(shí)候客戶(hù)端就通過(guò)投遞過(guò)來(lái)的消息 ID 來(lái)去重展示。

          以上步驟的數(shù)據(jù)流轉(zhuǎn)大致如圖所示:

          4.5、系統(tǒng)完整性設(shè)計(jì)

          4.5.1 可靠性

          (1)消息不丟失:

          為了避免消息丟失,我們?cè)O(shè)置了超時(shí)重傳機(jī)制。服務(wù)端會(huì)在推送給客戶(hù)端消息后,等待客戶(hù)端的 ACK,如果客戶(hù)端沒(méi)有返回 ACK,服務(wù)端會(huì)嘗試多次推送。

          目前默認(rèn) 18s 為超時(shí)時(shí)間,重傳 3 次不成功,斷開(kāi)連接,重新連接服務(wù)器。重新連接后,采用拉取歷史消息的機(jī)制來(lái)保證消息完整。

          (2)多端消息同步:

          客戶(hù)端現(xiàn)有 PC 瀏覽器、Windows 客戶(hù)端、H5、iOS/Android,系統(tǒng)允許用戶(hù)多端同時(shí)在線,且同一端可以多個(gè)狀態(tài),這就需要保證多端、多用戶(hù)、多狀態(tài)的消息是同步的。

          我們用到了 Redis 的 Hash 存儲(chǔ),將用戶(hù)信息、唯一連接對(duì)應(yīng)值 、連接標(biāo)識(shí)、客戶(hù)端 IP、服務(wù)器標(biāo)識(shí)、角色、渠道等記錄下來(lái),這樣通過(guò) key(uid) 就能找到一個(gè)用戶(hù)在多個(gè)端的連接,通過(guò) key+field 能定位到一條連接。

          4.5.2 可用性

          上文我們已經(jīng)說(shuō)過(guò),因?yàn)槭请p層設(shè)計(jì),就涉及到兩個(gè) Server 間的通信,同進(jìn)程內(nèi)通信用 Channel,非同進(jìn)程用消息隊(duì)列或者 RPC。綜合性能和對(duì)服務(wù)器資源利用,我們最終選擇 RPC 的方式進(jìn)行 Server 間通信。

          在對(duì)基于 Go 的 RPC 進(jìn)行選行時(shí),我們比較了以下比較主流的技術(shù)方案: 

          1)Go STDRPC:Go 標(biāo)準(zhǔn)庫(kù)的 RPC,性能最優(yōu),但是沒(méi)有治理;

          2)RPCX:性能優(yōu)勢(shì) 2*GRPC + 服務(wù)治理;

          3)GRPC:跨語(yǔ)言,但性能沒(méi)有 RPCX 好;

          4)TarsGo:跨語(yǔ)言,性能 5*GRPC,缺點(diǎn)是框架較大,整合起來(lái)費(fèi)勁;

          5)Dubbo-Go:性能稍遜一籌, 比較適合 Go 和 Java 間通信場(chǎng)景使用。

          最后我們選擇了 RPCX,因?yàn)樾阅芤埠芎茫灿蟹?wù)的治理。

          兩個(gè)進(jìn)程之間同樣需要通信,這里用到的是 ETCD 實(shí)現(xiàn)服務(wù)注冊(cè)發(fā)現(xiàn)機(jī)制。

          當(dāng)我們新增一個(gè) Worker,如果沒(méi)有注冊(cè)中心,就要用到配置文件來(lái)管理這些配置信息,這挺麻煩的。而且你新增一個(gè)后,需要分發(fā)模塊立刻發(fā)現(xiàn),不能有延遲。

          如果有新的服務(wù),分發(fā)模塊希望能快速感知到新的服務(wù)。利用 Key 的續(xù)租機(jī)制,如果在一定時(shí)間內(nèi),沒(méi)有監(jiān)聽(tīng)到 Key 有續(xù)租動(dòng)作,則認(rèn)為這個(gè)服務(wù)已經(jīng)掛掉,就會(huì)把該服務(wù)摘除。

          在進(jìn)行注冊(cè)中心的選型時(shí),我們主要調(diào)研了 ETCDZooKeeperConsul

          三者的壓測(cè)結(jié)果參考如下: 

           

          結(jié)果顯示,ETCD 的性能是最好的。另外,ETCD 背靠阿里巴巴,而且屬于 Go 生態(tài),我們公司內(nèi)部的 K8S 集群也在使用。

          綜合考量后,我們選擇使用 ETCD 作為服務(wù)注冊(cè)和發(fā)現(xiàn)組件。并且我們使用的是 ETCD 的集群模式,如果一臺(tái)服務(wù)器出現(xiàn)故障,集群其他的服務(wù)器仍能正常提供服務(wù)。

          小結(jié)一下:通過(guò)保證服務(wù)和進(jìn)程間的正常通訊,及 ETCD 集群模式的設(shè)計(jì),保證了 IM 服務(wù)整體具有極高的可用性。

          4.5.3 擴(kuò)展性

          消息分發(fā)模塊和消息處理模塊都能進(jìn)行水平擴(kuò)展。當(dāng)整體服務(wù)負(fù)載高時(shí),可以通過(guò)增加節(jié)點(diǎn)來(lái)分擔(dān)壓力,保證消息即時(shí)性和服務(wù)穩(wěn)定性。

          4.5.4 安全性

          處于安全性考慮,我們?cè)O(shè)置了黑名單機(jī)制,可以對(duì)單一 uid 或者 ip 進(jìn)行限制。比如在同一個(gè) uid 下,如果一段時(shí)間內(nèi)建立的連接次數(shù)超過(guò)設(shè)定的閾值,則認(rèn)為這個(gè) uid 可能存在風(fēng)險(xiǎn),暫停服務(wù)。如果暫停服務(wù)期間該 uid 繼續(xù)發(fā)送請(qǐng)求,則限制服務(wù)的時(shí)間相應(yīng)延長(zhǎng)。

          4.6、性能優(yōu)化和踩過(guò)的坑

          4.6.1 性能優(yōu)化

          1)JSON 編解碼:

          開(kāi)始我們使用官方的 JSON 編解碼工具,但由于對(duì)性能方面的追求,改為使用滴滴開(kāi)源的 Json-iterator,使在兼容原生 Golang 的 JSON 編解碼工具的同時(shí),效率上有比較明顯的提升。

          以下是壓測(cè)對(duì)比的參考圖: 

          2)time.After:

          在壓測(cè)的時(shí)候,我們發(fā)現(xiàn)內(nèi)存占用很高,于是使用 Go Tool PProf 分析 Golang 函數(shù)內(nèi)存申請(qǐng)情況,發(fā)現(xiàn)有不斷創(chuàng)建 time.After 定時(shí)器的問(wèn)題,定位到是心跳協(xié)程里面。

          原來(lái)代碼如下: 

          優(yōu)化后的代碼為:

          優(yōu)化點(diǎn)在于 for 循環(huán)里不要使用 select + time.After 的組合。

          3)Map 的使用:

          在保存連接信息的時(shí)候會(huì)用到 Map。因?yàn)橹白?TCP Socket 的項(xiàng)目的時(shí)候就遇到過(guò)一個(gè)坑,即 Map 在協(xié)程下是不安全的。當(dāng)多個(gè)協(xié)程同時(shí)對(duì)一個(gè) Map 進(jìn)行讀寫(xiě)時(shí),會(huì)拋出致命錯(cuò)誤:fetal error:concurrent map read and map write,有了這個(gè)經(jīng)驗(yàn)后,我們這里用的是 sync.Map

          4.6.2 踩坑經(jīng)驗(yàn)

          1)協(xié)程異常:

          基于對(duì)開(kāi)發(fā)成本和服務(wù)穩(wěn)定性等問(wèn)題的考慮,我們的 WebSocket 服務(wù)基于 Gorilla/WebSocket 框架開(kāi)發(fā)。其中遇到一個(gè)問(wèn)題,就是當(dāng)讀協(xié)程發(fā)生異常退出時(shí),寫(xiě)協(xié)程并沒(méi)有感知到,結(jié)果就是導(dǎo)致讀協(xié)程已經(jīng)退出但是寫(xiě)協(xié)程還在運(yùn)行,直到觸發(fā)異常之后才退出。

          這樣雖然從表面上看不影響業(yè)務(wù)邏輯,但是浪費(fèi)后端資源。在編碼時(shí)應(yīng)該注意要在讀協(xié)程退出后主動(dòng)通知寫(xiě)協(xié)程,這樣一個(gè)小的優(yōu)化可以這在高并發(fā)下能節(jié)省很多資源。

          2)心跳設(shè)計(jì):

          舉個(gè)例子:之前我們?cè)陂e時(shí)心跳功能的開(kāi)發(fā)中走了一些彎路。最初在服務(wù)器端的心跳發(fā)送是定時(shí)心跳,但后來(lái)在實(shí)際業(yè)務(wù)場(chǎng)景中使用時(shí)發(fā)現(xiàn),設(shè)計(jì)成服務(wù)器讀空閑時(shí)心跳更好。因?yàn)橛脩?hù)都在聊天呢,發(fā)一個(gè)心跳幀,浪費(fèi)感情也浪費(fèi)帶寬資源。

          這時(shí)候,建議大家在業(yè)務(wù)開(kāi)發(fā)過(guò)程中如果代碼寫(xiě)不下去就暫時(shí)不要寫(xiě)了,先結(jié)合業(yè)務(wù)需求用文字梳理下邏輯,可能會(huì)發(fā)現(xiàn)之后再進(jìn)行會(huì)更順利。

          3)每天分割日志: 

          日志模塊在起初調(diào)研的時(shí)候基于性能考慮,確定使用 Uber 開(kāi)源的 ZAP 庫(kù),而且滿(mǎn)足業(yè)務(wù)日志記錄的要求。日志庫(kù)選型很重要,選不好也是影響系統(tǒng)性能和穩(wěn)定性的。

          ZAP 的優(yōu)點(diǎn)包括:

          1)顯示代碼行號(hào)這個(gè)需求,ZAP 支持而 Logrus 不支持,這個(gè)屬于提效的。行號(hào)展示對(duì)于定位問(wèn)題很重要;

          2)ZAP 相對(duì)于 Logrus 更為高效,體現(xiàn)在寫(xiě) JSON 格式日志時(shí),沒(méi)有使用反射,而是用內(nèi)建的 json encoder,通過(guò)明確的類(lèi)型調(diào)用,直接拼接字符串,最小化性能開(kāi)銷(xiāo)。

          小坑:每天寫(xiě)一個(gè)日志文件的功能,目前 ZAP 不支持,需要自己寫(xiě)代碼支持,或者請(qǐng)求系統(tǒng)部支持。

          五、性能表現(xiàn)

          壓測(cè) 1:

          上線生產(chǎn)環(huán)境并和業(yè)務(wù)方對(duì)接以及壓測(cè),目前定制業(yè)務(wù)已接通整個(gè)流程,寫(xiě)了一個(gè) Client。模擬定期發(fā)心跳幀,然后利用 Docker 環(huán)境。開(kāi)啟了 50 個(gè)容器,每個(gè)容器模擬并發(fā)起 2 萬(wàn)個(gè)連接。這樣就是百萬(wàn)連接打到單機(jī)的 Server 上。單機(jī)內(nèi)存占用 30G 左右。

          壓測(cè) 2:

          同時(shí)并發(fā) 3000、4000、5000 連接,以及調(diào)整發(fā)送頻率,分別對(duì)應(yīng)上行:60萬(wàn)、80 萬(wàn)、100 萬(wàn)、200 萬(wàn), 一個(gè) 6k 左右的日志結(jié)構(gòu)體。

          其中有一半是心跳包 另一半是日志結(jié)構(gòu)體。在不同的壓力下的下行延遲數(shù)據(jù)如下: 

          結(jié)論:

          隨著上行的并發(fā)變大,延遲控制在 24-66 毫秒之間。所以對(duì)于下行業(yè)務(wù)屬于輕微延遲。另外針對(duì) 60 萬(wàn) 5k 上行的同時(shí),用另一個(gè)腳本模擬開(kāi)啟 50 個(gè)協(xié)程并發(fā)下行 1k 的數(shù)據(jù)體,延遲是比沒(méi)有并發(fā)下行的時(shí)候是有所提高的,延遲提高了 40ms 左右。

          六、本文小結(jié)

          基于 Go 重構(gòu)的 IM 服務(wù)在 WebSocket 的基礎(chǔ)上,將業(yè)務(wù)層設(shè)計(jì)為配有消息分發(fā)模塊和消息處理模塊的雙層架構(gòu)模式,使業(yè)務(wù)邏輯的處理前置,保證了即時(shí)通訊服務(wù)的純粹性和穩(wěn)定性;同時(shí)消息分發(fā)模塊的 HTTP 服務(wù)方便多種編程語(yǔ)言快速對(duì)接,使各業(yè)務(wù)線能迅速接入即時(shí)通訊服務(wù)。

          最后,我還想為 Go 搖旗吶喊一下。很多人都知道馬蜂窩技術(shù)體系主要是基于 PHP,有一些核心業(yè)務(wù)也在向 Java 遷移。與此同時(shí),Go 也在越來(lái)越多的項(xiàng)目中發(fā)揮作用。現(xiàn)在,云原生理念已經(jīng)逐漸成為主流趨勢(shì)之一,我們可以看到在很多構(gòu)建云原生應(yīng)用所需要的核心項(xiàng)目中,Go 都是主要的開(kāi)發(fā)語(yǔ)言,比如 Kubernetes,Docker,Istio,ETCD,Prometheus 等,包括第三代開(kāi)源分布式數(shù)據(jù)庫(kù) TiDB。

          所以我們可以把 Go 稱(chēng)為云原生時(shí)代的母語(yǔ)。「云原生時(shí)代,是開(kāi)發(fā)者最好的時(shí)代」,在這股浪潮下,我們?cè)皆缱哌M(jìn) Go,就可能越早在這個(gè)新時(shí)代搶占關(guān)鍵賽道。希望更多小伙伴和我們一起,加入到 Go 的開(kāi)發(fā)和學(xué)習(xí)陣營(yíng)中來(lái),拓寬自己的技能圖譜,擁抱云原生。

          附錄:更多IM架構(gòu)設(shè)計(jì)方面的文章

          [1] 有關(guān)IM架構(gòu)設(shè)計(jì)的文章:

          淺談IM系統(tǒng)的架構(gòu)設(shè)計(jì)

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

          一套海量在線用戶(hù)的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)

          一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案

          從零到卓越:京東客服即時(shí)通訊系統(tǒng)的技術(shù)架構(gòu)演進(jìn)歷程

          蘑菇街即時(shí)通訊/IM服務(wù)器開(kāi)發(fā)之架構(gòu)選擇

          騰訊QQ1.4億在線用戶(hù)的技術(shù)挑戰(zhàn)和架構(gòu)演進(jìn)之路PPT

          微信后臺(tái)基于時(shí)間序的海量數(shù)據(jù)冷熱分級(jí)架構(gòu)設(shè)計(jì)實(shí)踐

          微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡(jiǎn)(演講全文)

          如何解讀《微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡(jiǎn)》

          快速裂變:見(jiàn)證微信強(qiáng)大后臺(tái)架構(gòu)從0到1的演進(jìn)歷程(一)

          17年的實(shí)踐:騰訊海量產(chǎn)品的技術(shù)方法論

          移動(dòng)端IM中大規(guī)模群消息的推送如何保證效率、實(shí)時(shí)性?

          現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲(chǔ)方案探討

          IM開(kāi)發(fā)基礎(chǔ)知識(shí)補(bǔ)課(二):如何設(shè)計(jì)大量圖片文件的服務(wù)端存儲(chǔ)架構(gòu)?

          IM開(kāi)發(fā)基礎(chǔ)知識(shí)補(bǔ)課(三):快速理解服務(wù)端數(shù)據(jù)庫(kù)讀寫(xiě)分離原理及實(shí)踐建議

          IM開(kāi)發(fā)基礎(chǔ)知識(shí)補(bǔ)課(四):正確理解HTTP短連接中的Cookie、Session和Token

          WhatsApp技術(shù)實(shí)踐分享:32人工程團(tuán)隊(duì)創(chuàng)造的技術(shù)神話

          微信朋友圈千億訪問(wèn)量背后的技術(shù)挑戰(zhàn)和實(shí)踐總結(jié)

          王者榮耀2億用戶(hù)量的背后:產(chǎn)品定位、技術(shù)架構(gòu)、網(wǎng)絡(luò)方案等

          IM系統(tǒng)的MQ消息中間件選型:Kafka還是RabbitMQ?

          騰訊資深架構(gòu)師干貨總結(jié):一文讀懂大型分布式系統(tǒng)設(shè)計(jì)的方方面面

          以微博類(lèi)應(yīng)用場(chǎng)景為例,總結(jié)海量社交系統(tǒng)的架構(gòu)設(shè)計(jì)步驟

          快速理解高性能HTTP服務(wù)端的負(fù)載均衡技術(shù)原理

          子彈短信光鮮的背后:網(wǎng)易云信首席架構(gòu)師分享億級(jí)IM平臺(tái)的技術(shù)實(shí)踐

          知乎技術(shù)分享:從單機(jī)到2000萬(wàn)QPS并發(fā)的Redis高性能緩存實(shí)踐之路

          IM開(kāi)發(fā)基礎(chǔ)知識(shí)補(bǔ)課(五):通俗易懂,正確理解并用好MQ消息隊(duì)列

          微信技術(shù)分享:微信的海量IM聊天消息序列號(hào)生成實(shí)踐(算法原理篇)

          微信技術(shù)分享:微信的海量IM聊天消息序列號(hào)生成實(shí)踐(容災(zāi)方案篇)

          新手入門(mén):零基礎(chǔ)理解大型分布式架構(gòu)的演進(jìn)歷史、技術(shù)原理、最佳實(shí)踐

          一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐

          阿里技術(shù)分享:深度揭秘阿里數(shù)據(jù)庫(kù)技術(shù)方案的10年變遷史

          阿里技術(shù)分享:阿里自研金融級(jí)數(shù)據(jù)庫(kù)OceanBase的艱辛成長(zhǎng)之路

          社交軟件紅包技術(shù)解密(一):全面解密QQ紅包技術(shù)方案——架構(gòu)、技術(shù)實(shí)現(xiàn)等

          社交軟件紅包技術(shù)解密(二):解密微信搖一搖紅包從0到1的技術(shù)演進(jìn)

          社交軟件紅包技術(shù)解密(三):微信搖一搖紅包雨背后的技術(shù)細(xì)節(jié)

          社交軟件紅包技術(shù)解密(四):微信紅包系統(tǒng)是如何應(yīng)對(duì)高并發(fā)的

          社交軟件紅包技術(shù)解密(五):微信紅包系統(tǒng)是如何實(shí)現(xiàn)高可用性的

          社交軟件紅包技術(shù)解密(六):微信紅包系統(tǒng)的存儲(chǔ)層架構(gòu)演進(jìn)實(shí)踐

          社交軟件紅包技術(shù)解密(七):支付寶紅包的海量高并發(fā)技術(shù)實(shí)踐

          社交軟件紅包技術(shù)解密(八):全面解密微博紅包技術(shù)方案

          社交軟件紅包技術(shù)解密(九):談?wù)勈諵紅包的功能邏輯、容災(zāi)、運(yùn)維、架構(gòu)等

          即時(shí)通訊新手入門(mén):一文讀懂什么是Nginx?它能否實(shí)現(xiàn)IM的負(fù)載均衡?

          即時(shí)通訊新手入門(mén):快速理解RPC技術(shù)——基本概念、原理和用途

          多維度對(duì)比5款主流分布式MQ消息隊(duì)列,媽媽再也不擔(dān)心我的技術(shù)選型了

          從游擊隊(duì)到正規(guī)軍(一):馬蜂窩旅游網(wǎng)的IM系統(tǒng)架構(gòu)演進(jìn)之路

          從游擊隊(duì)到正規(guī)軍(二):馬蜂窩旅游網(wǎng)的IM客戶(hù)端架構(gòu)演進(jìn)和實(shí)踐總結(jié)

          IM開(kāi)發(fā)基礎(chǔ)知識(shí)補(bǔ)課(六):數(shù)據(jù)庫(kù)用NoSQL還是SQL?讀這篇就夠了!

          瓜子IM智能客服系統(tǒng)的數(shù)據(jù)架構(gòu)設(shè)計(jì)(整理自現(xiàn)場(chǎng)演講,有配套PPT)

          阿里釘釘技術(shù)分享:企業(yè)級(jí)IM王者——釘釘在后端架構(gòu)上的過(guò)人之處

          >> 更多同類(lèi)文章 ……

          [2] 更多其它架構(gòu)設(shè)計(jì)相關(guān)文章:

          騰訊資深架構(gòu)師干貨總結(jié):一文讀懂大型分布式系統(tǒng)設(shè)計(jì)的方方面面

          快速理解高性能HTTP服務(wù)端的負(fù)載均衡技術(shù)原理

          子彈短信光鮮的背后:網(wǎng)易云信首席架構(gòu)師分享億級(jí)IM平臺(tái)的技術(shù)實(shí)踐

          知乎技術(shù)分享:從單機(jī)到2000萬(wàn)QPS并發(fā)的Redis高性能緩存實(shí)踐之路

          新手入門(mén):零基礎(chǔ)理解大型分布式架構(gòu)的演進(jìn)歷史、技術(shù)原理、最佳實(shí)踐

          阿里技術(shù)分享:深度揭秘阿里數(shù)據(jù)庫(kù)技術(shù)方案的10年變遷史

          阿里技術(shù)分享:阿里自研金融級(jí)數(shù)據(jù)庫(kù)OceanBase的艱辛成長(zhǎng)之路

          達(dá)達(dá)O2O后臺(tái)架構(gòu)演進(jìn)實(shí)踐:從0到4000高并發(fā)請(qǐng)求背后的努力

          優(yōu)秀后端架構(gòu)師必會(huì)知識(shí):史上最全MySQL大表優(yōu)化方案總結(jié)

          小米技術(shù)分享:解密小米搶購(gòu)系統(tǒng)千萬(wàn)高并發(fā)架構(gòu)的演進(jìn)和實(shí)踐

          一篇讀懂分布式架構(gòu)下的負(fù)載均衡技術(shù):分類(lèi)、原理、算法、常見(jiàn)方案等

          通俗易懂:如何設(shè)計(jì)能支撐百萬(wàn)并發(fā)的數(shù)據(jù)庫(kù)架構(gòu)?

          多維度對(duì)比5款主流分布式MQ消息隊(duì)列,媽媽再也不擔(dān)心我的技術(shù)選型了

          從新手到架構(gòu)師,一篇就夠:從100到1000萬(wàn)高并發(fā)的架構(gòu)演進(jìn)之路

          美團(tuán)技術(shù)分享:深度解密美團(tuán)的分布式ID生成算法

          12306搶票帶來(lái)的啟示:看我如何用Go實(shí)現(xiàn)百萬(wàn)QPS的秒殺系統(tǒng)(含源碼)

          >> 更多同類(lèi)文章 ……

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

          posted @ 2020-02-19 16:47 Jack Jiang 閱讀(284) | 評(píng)論 (0)編輯 收藏

               摘要: 1、引言隨著Android系統(tǒng)的不斷升級(jí),即時(shí)通訊網(wǎng)技術(shù)群和社區(qū)里的IM和推送開(kāi)發(fā)的程序員們,對(duì)于進(jìn)程保活這件事是越來(lái)越悲觀,必竟系統(tǒng)對(duì)各種保活黑科技的限制越來(lái)越多了,想超越系統(tǒng)的摯肘,難度越來(lái)越大。但保活這件事就像“激情”之后的余味,總是讓人欲罷不能,想放棄又不甘心。那么,除了像上篇《2020年了,Android后臺(tái)保活還有戲嗎?看我如何優(yōu)雅的實(shí)現(xiàn)!》這樣的正經(jīng)白名單方式...  閱讀全文

          posted @ 2020-01-14 14:33 Jack Jiang 閱讀(250) | 評(píng)論 (0)編輯 收藏

               摘要: 1、引言掃碼登錄這個(gè)功能,最早應(yīng)該是微信的PC端開(kāi)始搞,雖然有點(diǎn)反人類(lèi)的功能(不掃碼也沒(méi)別的方式登錄),但不得不說(shuō)還是很酷的。下面這張圖,不管是IM開(kāi)發(fā)者還是普通用戶(hù),應(yīng)該很熟悉: 于是,搞IM產(chǎn)品的老板和產(chǎn)品經(jīng)理們,從此又多了一個(gè)要拋給程序員們的需求——“為什么微信有掃一掃登錄,而我們的沒(méi)有?”。好吧,每次只要是微信有的功能,IM程序員們...  閱讀全文

          posted @ 2020-01-08 13:39 Jack Jiang 閱讀(128) | 評(píng)論 (0)編輯 收藏

               摘要: 本文由騰訊WXG應(yīng)用研究員breezecheng原創(chuàng)發(fā)表于公眾號(hào)“騰訊技術(shù)工程”,原題“微信「掃一掃識(shí)物」 的背后技術(shù)揭秘”。一、引言現(xiàn)在市面上主流的移動(dòng)端IM應(yīng)用于都有“掃一掃”功能,看起來(lái)好像也就能掃一掃加好友、加群,但實(shí)際上作為一個(gè)IM產(chǎn)品的重要信息入口,“掃一掃”功能也可以很強(qiáng)大。▲...  閱讀全文

          posted @ 2020-01-02 20:54 Jack Jiang 閱讀(210) | 評(píng)論 (0)編輯 收藏

          1、引言

          對(duì)于移動(dòng)端IM應(yīng)用和消息推送應(yīng)用的開(kāi)發(fā)者來(lái)說(shuō),Android后臺(tái)保活這件事是再熟悉不過(guò)了。

          自從Android P(即Android 8.0)出現(xiàn)以后,Android已經(jīng)從系統(tǒng)層面將后臺(tái)保活這條路給堵死了(詳見(jiàn):《Android P正式版即將到來(lái):后臺(tái)應(yīng)用保活、消息推送的真正噩夢(mèng)》),曾今那些層出不窮的保活黑科技能用的也越來(lái)越少了(詳見(jiàn):《全面盤(pán)點(diǎn)當(dāng)前Android后臺(tái)保活方案的真實(shí)運(yùn)行效果(截止2019年前)》。雖然可以自已對(duì)接廠商的ROOM級(jí)推送通道,但一方面各廠商的推送接口都不一樣(而且同一廠商不同的系統(tǒng)版本間也存在推送接口的兼容性問(wèn)題),很不方便。另一方面要一家家引入各自的推送服務(wù)SDK包會(huì)讓APP變的很大,這讓APP的下載變的很不友好。

          總之,Android應(yīng)用的后臺(tái)保活在某些場(chǎng)景下,還是有持續(xù)的需求。除了之前那些耳熟能詳?shù)谋;詈诳萍家酝猓贏ndroid 9.0(甚至Android 10)時(shí)代,我們還有哪些保活方法可以用?那么,請(qǐng)跟著本文作者的思路,看看更優(yōu)雅的后臺(tái)保活實(shí)現(xiàn)方法吧。

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

           

          2、關(guān)于作者

          網(wǎng)名NanBox:畢業(yè)于華中科技大學(xué),現(xiàn)為"悅跑圈APP”高級(jí)Android開(kāi)發(fā)工程師。主要負(fù)責(zé)公司 Android 項(xiàng)目,核心模塊的開(kāi)發(fā)。涉及 GPS 定位、地圖、圖片編輯等功能。獨(dú)立開(kāi)發(fā)了手表應(yīng)用項(xiàng)目。 在項(xiàng)目中應(yīng)入了 Flutter 跨平臺(tái)開(kāi)發(fā)技術(shù),實(shí)現(xiàn)了原生和 Flutter 的混合開(kāi)發(fā)。

          本文作者樂(lè)于分享,平時(shí)會(huì)寫(xiě)技術(shù)文章并分享在多個(gè)平臺(tái),是掘金專(zhuān)欄作者的一員,文章總閱讀量超過(guò) 10 萬(wàn)。在 GitHub 上有多個(gè)開(kāi)源項(xiàng)目,多次在團(tuán)隊(duì)內(nèi)部進(jìn)行技術(shù)分享。是 Android 和 Flutter 官方中文文檔譯者。

          3、相關(guān)文章

          如果你想詳細(xì)了解目前Android平臺(tái)上后臺(tái)保活技術(shù)的挑戰(zhàn),請(qǐng)閱讀:

          Android P正式版即將到來(lái):后臺(tái)應(yīng)用保活、消息推送的真正噩夢(mèng)》。

          如果你想回顧那些曾今出現(xiàn)的Android保活黑科技,以下文章值得好好讀讀:

          全面盤(pán)點(diǎn)當(dāng)前Android后臺(tái)保活方案的真實(shí)運(yùn)行效果(截止2019年前)

          應(yīng)用保活終極總結(jié)(一):Android6.0以下的雙進(jìn)程守護(hù)保活實(shí)踐

          應(yīng)用保活終極總結(jié)(二):Android6.0及以上的保活實(shí)踐(進(jìn)程防殺篇)

          應(yīng)用保活終極總結(jié)(三):Android6.0及以上的保活實(shí)踐(被殺復(fù)活篇)

          Android進(jìn)程保活詳解:一篇文章解決你的所有疑問(wèn)

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

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

          為何基于TCP協(xié)議的移動(dòng)端IM仍然需要心跳保活機(jī)制?

          微信團(tuán)隊(duì)原創(chuàng)分享:Android版微信后臺(tái)保活實(shí)戰(zhàn)分享(進(jìn)程保活篇)

          融云技術(shù)分享:融云安卓端IM產(chǎn)品的網(wǎng)絡(luò)鏈路保活技術(shù)實(shí)踐

          4、Android保活現(xiàn)狀

          我們知道,Android 系統(tǒng)會(huì)存在殺后臺(tái)進(jìn)程的情況,并且隨著系統(tǒng)版本的更新,殺進(jìn)程的力度還有越來(lái)越大的趨勢(shì)(見(jiàn):《Android P正式版即將到來(lái):后臺(tái)應(yīng)用保活、消息推送的真正噩夢(mèng)》)。

          系統(tǒng)這種做法本身出發(fā)點(diǎn)是好的,因?yàn)榭梢怨?jié)省內(nèi)存,降低功耗,也避免了一些流氓行為。

          但有一部分應(yīng)用,應(yīng)用本身的使用場(chǎng)景就需要在后臺(tái)運(yùn)行,用戶(hù)也是愿意讓它在后臺(tái)運(yùn)行的,比如跑步類(lèi)應(yīng)用、一些懶得對(duì)接廠商推送通道的IM應(yīng)用、消息推送資訊類(lèi)應(yīng)用等。

          一方面流氓軟件用各種流氓手段進(jìn)行保活,另一方面系統(tǒng)加大殺后臺(tái)的力度,導(dǎo)致我們一些真正需要在后臺(tái)運(yùn)行的應(yīng)用被誤殺,苦不堪言。

          5、優(yōu)雅的保活?

          為了做到保活,出現(xiàn)了不少「黑科技」,比如 1 個(gè)像素的 Activity,播放無(wú)聲音頻,雙進(jìn)程互相守護(hù)等(可以讀讀這個(gè)系列:《應(yīng)用保活終極總結(jié)(一):Android6.0以下的雙進(jìn)程守護(hù)保活實(shí)踐》、《應(yīng)用保活終極總結(jié)(二):Android6.0及以上的保活實(shí)踐(進(jìn)程防殺篇)》、《應(yīng)用保活終極總結(jié)(三):Android6.0及以上的保活實(shí)踐(被殺復(fù)活篇)》)。

          這些做法可以說(shuō)是很流氓了,甚至破壞了 Android 的生態(tài),好在隨著 Android 系統(tǒng)版本的更新,這些非常規(guī)的保活手段很多都已失效了。

          對(duì)于那些確實(shí)需要在后臺(tái)運(yùn)行的應(yīng)用,我們?nèi)绾巫龅絻?yōu)雅的保活呢?

          6、加入后臺(tái)運(yùn)行白名單,可以?xún)?yōu)雅的實(shí)現(xiàn)保活

          從 Android 6.0 開(kāi)始,系統(tǒng)為了省電增加了休眠模式,系統(tǒng)待機(jī)一段時(shí)間后,會(huì)殺死后臺(tái)正在運(yùn)行的進(jìn)程。但系統(tǒng)會(huì)有一個(gè)后臺(tái)運(yùn)行白名單,白名單里的應(yīng)用將不會(huì)受到影響,在原生系統(tǒng)下,通過(guò):「設(shè)置」 - 「電池」 - 「電池優(yōu)化」 - 「未優(yōu)化應(yīng)用」,可以看到這個(gè)白名單。

          通常會(huì)看到下面這兩位: 

          下次被產(chǎn)品說(shuō)「 XXX 都可以保活,為什么我們不行!」的時(shí)候,你就知道怎么懟回去了。大廠通過(guò)和手機(jī)廠商的合作,將自己的應(yīng)用默認(rèn)加入到白名單中。如果你在一個(gè)能談成這種合作的大廠,也就不用往下看了。

          好在系統(tǒng)還沒(méi)有拋棄我們,允許我們申請(qǐng)把應(yīng)用加入白名單。

          首先,在 AndroidManifest.xml 文件中配置一下權(quán)限:

          <uses-permissionandroid:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>

          可以通過(guò)以下方法,判斷我們的應(yīng)用是否在白名單中:

          @RequiresApi(api = Build.VERSION_CODES.M)

          private boolean isIgnoringBatteryOptimizations() {

              boolean isIgnoring = false;

              PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);

              if(powerManager != null) {

                  isIgnoring = powerManager.isIgnoringBatteryOptimizations(getPackageName());

              }

              return isIgnoring;

          }

          如果不在白名單中,可以通過(guò)以下代碼申請(qǐng)加入白名單:

          @RequiresApi(api = Build.VERSION_CODES.M)

          public void requestIgnoreBatteryOptimizations() {

              try{

                  Intent intent = newIntent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);

                  intent.setData(Uri.parse("package:"+ getPackageName()));

                  startActivity(intent);

              } catch(Exception e) {

                  e.printStackTrace();

              }

          }

          申請(qǐng)時(shí),應(yīng)用上會(huì)出現(xiàn)這樣一個(gè)窗口:

          可以看到,這個(gè)系統(tǒng)彈窗會(huì)有影響電池續(xù)航的提醒,所以如果想讓用戶(hù)點(diǎn)允許,必須要有相關(guān)的說(shuō)明。如果要判斷用戶(hù)是否點(diǎn)擊了允許,可以在申請(qǐng)的時(shí)候調(diào)用 startActivityForResult,在 onActivityResult 里再判斷一次是否在白名單中。

          7、加入后臺(tái)運(yùn)行白名單的多廠商適配方法

          7.1 基本說(shuō)明

          Android 開(kāi)發(fā)的一個(gè)難點(diǎn)在于,各大手機(jī)廠商對(duì)原生系統(tǒng)進(jìn)行了不同的定制,導(dǎo)致我們需要進(jìn)行不同的適配,后臺(tái)管理就是一個(gè)很好的體現(xiàn)。幾乎各個(gè)廠商都有自己的后臺(tái)管理,就算應(yīng)用加入了后臺(tái)運(yùn)行白名單,仍然可能會(huì)被廠商自己的后臺(tái)管理干掉。

          如果能把應(yīng)用加入廠商系統(tǒng)的后臺(tái)管理白名單,可以進(jìn)一步降低進(jìn)程被殺的概率。不同的廠商在不同的地方進(jìn)行設(shè)置,一般是在各自的「手機(jī)管家」,但更難的是,就算同一個(gè)廠商的系統(tǒng),不同的版本也可能是在不同地方設(shè)置。

          最理想的做法是,我們根據(jù)不同手機(jī),甚至是不同的系統(tǒng)版本,給用戶(hù)呈現(xiàn)一個(gè)圖文操作步驟,并且提供一個(gè)按鈕,直接跳轉(zhuǎn)到指定頁(yè)面進(jìn)行設(shè)置。但需要對(duì)每個(gè)廠商每個(gè)版本進(jìn)行適配,工作量是比較大的。我使用真機(jī)測(cè)試了大部分主流 Android 廠商的手機(jī)后,整理出了部分手機(jī)的相關(guān)資料。

          首先我們可以定義這樣兩個(gè)方法:

          /**

           * 跳轉(zhuǎn)到指定應(yīng)用的首頁(yè)

           */

          private void showActivity(@NonNull String packageName) {

              Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);

              startActivity(intent);

          }

           

          /**

           * 跳轉(zhuǎn)到指定應(yīng)用的指定頁(yè)面

           */

          private void showActivity(@NonNull String packageName, @NonNull String activityDir) {

              Intent intent = new Intent();

              intent.setComponent(newComponentName(packageName, activityDir));

              intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

              startActivity(intent);

          }

          以下是部分手機(jī)的廠商判斷,跳轉(zhuǎn)方法及對(duì)應(yīng)設(shè)置步驟,跳轉(zhuǎn)方法不保證在所有版本上都能成功跳轉(zhuǎn),都需要加 try catch。

          7.2 華為

          廠商判斷:

          public boolean isHuawei() {

              if(Build.BRAND == null) {

                  return false;

              } else{

                  return Build.BRAND.toLowerCase().equals("huawei") || Build.BRAND.toLowerCase().equals("honor");

              }

          }

          跳轉(zhuǎn)華為手機(jī)管家的啟動(dòng)管理頁(yè):

          private void goHuaweiSetting() {

              try{

                  showActivity("com.huawei.systemmanager",

                      "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");

              } catch(Exception e) {

                  showActivity("com.huawei.systemmanager",

                      "com.huawei.systemmanager.optimize.bootstart.BootStartActivity");

              }

          }

          操作步驟:應(yīng)用啟動(dòng)管理 -> 關(guān)閉應(yīng)用開(kāi)關(guān) -> 打開(kāi)允許自啟動(dòng)

          7.3 小米

          廠商判斷:

          public static boolean isXiaomi() {

              return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("xiaomi");

          }

          跳轉(zhuǎn)小米安全中心的自啟動(dòng)管理頁(yè)面:

          private void goXiaomiSetting() {

              showActivity("com.miui.securitycenter",

                  "com.miui.permcenter.autostart.AutoStartManagementActivity");

          }

          操作步驟:授權(quán)管理 -> 自啟動(dòng)管理 -> 允許應(yīng)用自啟動(dòng)

          7.4 OPPO

          廠商判斷:

          public static boolean isOPPO() {

              return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("oppo");

          }

          跳轉(zhuǎn) OPPO 手機(jī)管家:

          private void goOPPOSetting() {

              try{

                  showActivity("com.coloros.phonemanager");

              } catch(Exception e1) {

                  try{

                      showActivity("com.oppo.safe");

                  } catch(Exception e2) {

                      try{

                          showActivity("com.coloros.oppoguardelf");

                      } catch(Exception e3) {

                          showActivity("com.coloros.safecenter");

                      }

                  }

              }

          }

          操作步驟:權(quán)限隱私 -> 自啟動(dòng)管理 -> 允許應(yīng)用自啟動(dòng)

          7.5 VIVO

          廠商判斷:

          public static boolean isVIVO() {

              return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("vivo");

          }

          跳轉(zhuǎn) VIVO 手機(jī)管家:

          private void goVIVOSetting() {

              showActivity("com.iqoo.secure");

          }

          操作步驟:權(quán)限管理 -> 自啟動(dòng) -> 允許應(yīng)用自啟動(dòng)

          7.6 魅族

          廠商判斷:

          public static boolean isMeizu() {

              return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("meizu");

          }

          跳轉(zhuǎn)魅族手機(jī)管家:

          private void goMeizuSetting() {

              showActivity("com.meizu.safe");

          }

          操作步驟:權(quán)限管理 -> 后臺(tái)管理 -> 點(diǎn)擊應(yīng)用 -> 允許后臺(tái)運(yùn)行

          7.7 三星

          廠商判斷:

          public static boolean isSamsung() {

              return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("samsung");

          }

          跳轉(zhuǎn)三星智能管理器:

          private void goSamsungSetting() {

              try{

                  showActivity("com.samsung.android.sm_cn");

              } catch(Exception e) {

                  showActivity("com.samsung.android.sm");

              }

          }

          操作步驟:自動(dòng)運(yùn)行應(yīng)用程序 -> 打開(kāi)應(yīng)用開(kāi)關(guān) -> 電池管理 -> 未監(jiān)視的應(yīng)用程序 -> 添加應(yīng)用

          7.8 樂(lè)視

          廠商判斷:

          public static boolean isLeTV() {

              return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("letv");

          }

          跳轉(zhuǎn)樂(lè)視手機(jī)管家:

          private void goLetvSetting() {

              showActivity("com.letv.android.letvsafe",

                  "com.letv.android.letvsafe.AutobootManageActivity");

          }

          操作步驟:自啟動(dòng)管理 -> 允許應(yīng)用自啟動(dòng)

          7.9 錘子

          廠商判斷:

          public static boolean isSmartisan() {

              return Build.BRAND != null&& Build.BRAND.toLowerCase().equals("smartisan");

          }

          跳轉(zhuǎn)手機(jī)管理:

          private void goSmartisanSetting() {

              showActivity("com.smartisanos.security");

          }

          操作步驟:權(quán)限管理 -> 自啟動(dòng)權(quán)限管理 -> 點(diǎn)擊應(yīng)用 -> 允許被系統(tǒng)啟動(dòng)。

          8、友商致敬?

          在之前做的跑步應(yīng)用中,我在設(shè)置里增加了一個(gè)權(quán)限設(shè)置頁(yè)面,將上面提到的設(shè)置放在這里面。

          最近發(fā)現(xiàn)友商某咚也跟進(jìn)了,圖 1 是我們做的,圖 2 是某咚做的: 

          某咚從設(shè)計(jì)、從我寫(xiě)的不夠好的文案,甚至是我從十幾臺(tái)手機(jī)上一張一張截下來(lái)的圖,進(jìn)行了全方位的致敬。感謝某咚的認(rèn)可,但最近在某個(gè)發(fā)布會(huì)上聽(tīng)到這么一句話:在致敬的同時(shí),能不能說(shuō)一句謝謝?

          某咚的致敬,一方面說(shuō)明了目前確實(shí)存在進(jìn)程容易被殺,保活難度大的問(wèn)題,另一方面也說(shuō)明了這種引導(dǎo)用戶(hù)進(jìn)行白名單設(shè)置的手段是有效的。

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

          應(yīng)用保活終極總結(jié)(一):Android6.0以下的雙進(jìn)程守護(hù)保活實(shí)踐

          應(yīng)用保活終極總結(jié)(二):Android6.0及以上的保活實(shí)踐(進(jìn)程防殺篇)

          應(yīng)用保活終極總結(jié)(三):Android6.0及以上的保活實(shí)踐(被殺復(fù)活篇)

          Android進(jìn)程保活詳解:一篇文章解決你的所有疑問(wèn)

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

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

          為何基于TCP協(xié)議的移動(dòng)端IM仍然需要心跳保活機(jī)制?

          微信團(tuán)隊(duì)原創(chuàng)分享:Android版微信后臺(tái)保活實(shí)戰(zhàn)分享(進(jìn)程保活篇)

          微信團(tuán)隊(duì)原創(chuàng)分享:Android版微信后臺(tái)保活實(shí)戰(zhàn)分享(網(wǎng)絡(luò)保活篇)

          移動(dòng)端IM實(shí)踐:實(shí)現(xiàn)Android版微信的智能心跳機(jī)制

          移動(dòng)端IM實(shí)踐:WhatsApp、Line、微信的心跳策略分析

          Android P正式版即將到來(lái):后臺(tái)應(yīng)用保活、消息推送的真正噩夢(mèng)

          全面盤(pán)點(diǎn)當(dāng)前Android后臺(tái)保活方案的真實(shí)運(yùn)行效果(截止2019年前)

          一文讀懂即時(shí)通訊應(yīng)用中的網(wǎng)絡(luò)心跳包機(jī)制:作用、原理、實(shí)現(xiàn)思路等

          融云技術(shù)分享:融云安卓端IM產(chǎn)品的網(wǎng)絡(luò)鏈路保活技術(shù)實(shí)踐

          正確理解IM長(zhǎng)連接的心跳及重連機(jī)制,并動(dòng)手實(shí)現(xiàn)(有完整IM源碼)

          2020年了,Android后臺(tái)保活還有戲嗎?看我如何優(yōu)雅的實(shí)現(xiàn)!

          >> 更多同類(lèi)文章 ……

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

          posted @ 2019-12-27 14:51 Jack Jiang 閱讀(423) | 評(píng)論 (0)編輯 收藏

               摘要: 原作者:黃日成,手Q游戲中心后臺(tái)開(kāi)發(fā),騰訊高級(jí)工程師。從事C++服務(wù)后臺(tái)開(kāi)發(fā)4年多,主要負(fù)責(zé)手Q游戲中心后臺(tái)基礎(chǔ)系統(tǒng)、復(fù)雜業(yè)務(wù)系統(tǒng)開(kāi)發(fā),主導(dǎo)過(guò)手Q游戲公會(huì)、企鵝電競(jìng)App-對(duì)戰(zhàn)系統(tǒng)等項(xiàng)目的后臺(tái)系統(tǒng)設(shè)計(jì),有豐富的后臺(tái)架構(gòu)經(jīng)驗(yàn)。1、引言接本系列的上一篇《P2P技術(shù)詳解(二):P2P中的NAT穿越(打洞)方案詳解(基本原理篇)》,本篇將深入分析各種NAT穿越(打洞)方案的技術(shù)實(shí)現(xiàn)原理和數(shù)據(jù)交互過(guò)程,希望...  閱讀全文

          posted @ 2019-12-24 11:25 Jack Jiang 閱讀(299) | 評(píng)論 (0)編輯 收藏

               摘要: 1、引言歲月真是個(gè)養(yǎng)豬場(chǎng),這幾年,人胖了,微信代碼也翻了。記得 14 年轉(zhuǎn)崗來(lái)微信時(shí),用自己筆記本編譯微信工程才十來(lái)分鐘。如今用公司配的 17 年款 27-inch iMac 編譯要接近半小時(shí);偶然間更新完代碼,又莫名其妙需要全新編譯。在這么低的編譯效率下,開(kāi)發(fā)心情受到嚴(yán)重影響。于是年初我向上頭請(qǐng)示,優(yōu)化微信編譯效率,上頭也同意了。  學(xué)習(xí)交流:- 即時(shí)通訊/推送技術(shù)開(kāi)發(fā)交流5...  閱讀全文

          posted @ 2019-12-19 20:22 Jack Jiang 閱讀(144) | 評(píng)論 (0)編輯 收藏

               摘要: 1、引言IM等社交應(yīng)用的開(kāi)發(fā)工作中,亂碼問(wèn)題也很常見(jiàn),比如:1)IM聊天消息中的Emoji表情為什么發(fā)給后端后MySQL數(shù)據(jù)庫(kù)里會(huì)亂碼;2)文件名中帶有中文的大文件聊天消息發(fā)送后,對(duì)方看到的文名是亂碼;3)Http rest接口調(diào)用時(shí),后端讀取到APP端傳過(guò)來(lái)的參數(shù)有中文亂碼問(wèn)題;... ...那么,對(duì)于亂碼這個(gè)看似不起眼,但并不是一兩話能講清楚的問(wèn)題,是很有必要從根源了解字符集和編碼原理,知其然...  閱讀全文

          posted @ 2019-12-17 19:36 Jack Jiang 閱讀(183) | 評(píng)論 (0)編輯 收藏

          僅列出標(biāo)題
          共50頁(yè): First 上一頁(yè) 30 31 32 33 34 35 36 37 38 下一頁(yè) Last 
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 三穗县| 乌拉特前旗| 红原县| 颍上县| 九江县| 城步| 宜春市| 敦煌市| 宁阳县| 安顺市| 龙里县| 且末县| 宁河县| 库尔勒市| 珠海市| 青田县| 吕梁市| 迭部县| 连州市| 双辽市| 同江市| 墨竹工卡县| 犍为县| 宜昌市| 涿州市| 额敏县| 余姚市| 岳池县| 汾西县| 宜州市| 喀什市| 龙口市| 会宁县| 东阳市| 长葛市| 德保县| 鹿邑县| 忻城县| 南丰县| 大丰市| 泽州县|