基于Go的马蜂窝旅游网分布式IM系统技术实践
個(gè)人博客導(dǎo)航頁(yè)(點(diǎn)擊右側(cè)鏈接即可打開(kāi)個(gè)人博客):互聯(lián)網(wǎng)老兵帶你入門(mén)技術(shù)棧?
一、引言
即時(shí)通訊(IM)功能對(duì)于電商平臺(tái)來(lái)說(shuō)非常重要,特別是旅游電商。
從商品復(fù)雜性來(lái)看,一個(gè)旅游商品可能會(huì)包括用戶在未來(lái)一段時(shí)間的衣、食、住、行等方方面面。從消費(fèi)金額來(lái)看,往往單次消費(fèi)額度較大。對(duì)目的地的陌生、在行程中可能的問(wèn)題,這些因素使用戶在購(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ì)到正規(guī)軍(一):馬蜂窩旅游網(wǎng)的IM系統(tǒng)架構(gòu)演進(jìn)之路》
《從游擊隊(duì)到正規(guī)軍(二):馬蜂窩旅游網(wǎng)的IM客戶端架構(gòu)演進(jìn)和實(shí)踐總結(jié)》
《從游擊隊(duì)到正規(guī)軍(三):基于Go的馬蜂窩旅游網(wǎng)分布式IM系統(tǒng)技術(shù)實(shí)踐》(* 本文)
二、技術(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)用中的消息推送主要基于輪詢技術(shù),消息輪詢模塊的長(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)景下還是要依賴 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)
3.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ò)展及提升的能力。
3.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ě)代碼了。
3.3、架構(gòu)設(shè)計(jì)
整體架構(gòu)圖如下:?
名詞解釋:
- 1)客戶:一般指購(gòu)買(mǎi)商品的用戶;
- 2)商家:提供服務(wù)的供應(yīng)商,商家會(huì)有客服人員,提供給客戶一個(gè)在線咨詢的作用;
- 3)分發(fā)模塊:即 Dispatcher,提供消息分發(fā)的給指定的工作模塊的橋接作用;
- 4)工作模塊:即 Worker 服務(wù)器,用來(lái)提供 WebSocket 服務(wù),是真正工作的一個(gè)模塊。
架構(gòu)分層:
- 1)展示層:提供 HTTP 和 WebSocket 兩種接入方式;
- 2)業(yè)務(wù)層:負(fù)責(zé)初始化消息線和業(yè)務(wù)邏輯處理。如果客戶端以 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 把消息推送給客戶端;
- 4)數(shù)據(jù)層:Redis 集群,記錄用戶身份、連接信息、客戶端平臺(tái)(移動(dòng)端、網(wǎng)頁(yè)端、桌面端)等組成的唯一 Key。
3.4、服務(wù)流程
步驟一:
如上圖右側(cè)所示:
用戶客戶端與消息處理模塊建立 WebSocket 長(zhǎng)連接;
通過(guò)負(fù)載均衡算法,使客戶端連接到合適的服務(wù)器(消息處理模塊的某個(gè) Worker);
連接成功后,記錄用戶連接信息,包括用戶角色(客人或商家)、客戶端平臺(tái)(移動(dòng)端、網(wǎng)頁(yè)端、桌面端)等組成唯一 Key,記錄到 Redis 集群。
步驟二:
如圖左側(cè)所示,當(dāng)購(gòu)買(mǎi)商品的用戶要給管家發(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ǔ)言的客戶端。通過(guò) JSON 格式把消息發(fā)送給業(yè)務(wù)服務(wù)器,業(yè)務(wù)服務(wù)器先把消息解碼,然后拿到這個(gè)用戶要發(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 集群中的用戶連接信息,消息分發(fā)模塊將消息轉(zhuǎn)發(fā)到目標(biāo)用戶連接的 WebSocket 服務(wù)器(消息處理模塊中的某一個(gè) Worker)
1)分發(fā)模塊通過(guò) RPC 方式把消息轉(zhuǎn)發(fā)到目標(biāo)用戶連接的 Worker,RPC 的方式性能更快,而且傳輸?shù)臄?shù)據(jù)也少,從而節(jié)約了服務(wù)器的成本。
2)消息透?jìng)?Worker 的時(shí)候,多種策略保障消息一定會(huì)下發(fā)到 Worker。
步驟五:
消息處理模塊將消息通過(guò) WebSocket 協(xié)議推送到客戶端。
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ā)送給客戶端,客戶端也收到了,但是因?yàn)榫W(wǎng)絡(luò)抖動(dòng),沒(méi)有把 ACK 信息發(fā)送給服務(wù)器,那服務(wù)器會(huì)重復(fù)投遞給客戶端,這時(shí)候客戶端就通過(guò)投遞過(guò)來(lái)的消息 ID 來(lái)去重展示。
以上步驟的數(shù)據(jù)流轉(zhuǎn)大致如圖所示:
3.5、系統(tǒng)完整性設(shè)計(jì)
3.5.1 可靠性
(1)消息不丟失:
為了避免消息丟失,我們?cè)O(shè)置了超時(shí)重傳機(jī)制。服務(wù)端會(huì)在推送給客戶端消息后,等待客戶端的 ACK,如果客戶端沒(méi)有返回 ACK,服務(wù)端會(huì)嘗試多次推送。
目前默認(rèn) 18s 為超時(shí)時(shí)間,重傳 3 次不成功,斷開(kāi)連接,重新連接服務(wù)器。重新連接后,采用拉取歷史消息的機(jī)制來(lái)保證消息完整。
(2)多端消息同步:
客戶端現(xiàn)有 PC 瀏覽器、Windows 客戶端、H5、iOS/Android,系統(tǒng)允許用戶多端同時(shí)在線,且同一端可以多個(gè)狀態(tài),這就需要保證多端、多用戶、多狀態(tài)的消息是同步的。
我們用到了 Redis 的 Hash 存儲(chǔ),將用戶信息、唯一連接對(duì)應(yīng)值 、連接標(biāo)識(shí)、客戶端 IP、服務(wù)器標(biāo)識(shí)、角色、渠道等記錄下來(lái),這樣通過(guò) key(uid) 就能找到一個(gè)用戶在多個(gè)端的連接,通過(guò) key+field 能定位到一條連接。
3.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)樾阅芤埠芎?#xff0c;也有服務(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)研了?ETCD、ZooKeeper、Consul。
三者的壓測(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ù)整體具有極高的可用性。
3.5.3 擴(kuò)展性
消息分發(fā)模塊和消息處理模塊都能進(jìn)行水平擴(kuò)展。當(dāng)整體服務(wù)負(fù)載高時(shí),可以通過(guò)增加節(jié)點(diǎn)來(lái)分擔(dān)壓力,保證消息即時(shí)性和服務(wù)穩(wěn)定性。
3.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)。
3.6、性能優(yōu)化和踩過(guò)的坑
3.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
3.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)橛脩舳荚诹奶炷?#xff0c;發(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ù),而且滿足業(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 稱為云原生時(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),拓寬自己的技能圖譜,擁抱云原生。(本文同步發(fā)布于:http://www.52im.net/thread-2909-1-1.html)
附Java/C/C++/機(jī)器學(xué)習(xí)/算法與數(shù)據(jù)結(jié)構(gòu)/前端/安卓/Python/程序員必讀/書(shū)籍書(shū)單大全:
(點(diǎn)擊右側(cè) 即可打開(kāi)個(gè)人博客內(nèi)有干貨):技術(shù)干貨小棧
=====>>①【Java大牛帶你入門(mén)到進(jìn)階之路】<<====
=====>>②【算法數(shù)據(jù)結(jié)構(gòu)+acm大牛帶你入門(mén)到進(jìn)階之路】<<===
=====>>③【數(shù)據(jù)庫(kù)大牛帶你入門(mén)到進(jìn)階之路】<<=====
=====>>④【W(wǎng)eb前端大牛帶你入門(mén)到進(jìn)階之路】<<====
=====>>⑤【機(jī)器學(xué)習(xí)和python大牛帶你入門(mén)到進(jìn)階之路】<<====
=====>>⑥【架構(gòu)師大牛帶你入門(mén)到進(jìn)階之路】<<=====
=====>>⑦【C++大牛帶你入門(mén)到進(jìn)階之路】<<====
=====>>⑧【ios大牛帶你入門(mén)到進(jìn)階之路】<<====
=====>>⑨【W(wǎng)eb安全大牛帶你入門(mén)到進(jìn)階之路】<<=====
=====>>⑩【Linux和操作系統(tǒng)大牛帶你入門(mén)到進(jìn)階之路】<<=====
天下沒(méi)有不勞而獲的果實(shí),望各位年輕的朋友,想學(xué)技術(shù)的朋友,在決心扎入技術(shù)道路的路上披荊斬棘,把書(shū)弄懂了,再去敲代碼,把原理弄懂了,再去實(shí)踐,將會(huì)帶給你的人生,你的工作,你的未來(lái)一個(gè)美夢(mèng)。
總結(jié)
以上是生活随笔為你收集整理的基于Go的马蜂窝旅游网分布式IM系统技术实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 高等数学(第七版)同济大学 总习题六 个
- 下一篇: 萤火虫算法_一种优化方法:蜂鸟优化算法