拉模式和推模式區(qū)別
拉模式(定時(shí)輪詢?cè)L問接口獲取數(shù)據(jù))
1. 數(shù)據(jù)更新頻率低,則大多數(shù)的數(shù)據(jù)請(qǐng)求時(shí)無效的
2. 在線用戶數(shù)量多,則服務(wù)端的查詢負(fù)載很高
3. 定時(shí)輪詢拉取,無法滿足時(shí)效性要求
推模式(向客戶端進(jìn)行數(shù)據(jù)的推送)
1. 僅在數(shù)據(jù)更新時(shí),才有推送
2. 需要維護(hù)大量的在線長連接
3. 數(shù)據(jù)更新后,可以立即推送
基于WebSocket協(xié)議做推送
1. 瀏覽器支持的socket編程,輕松維持服務(wù)端的長連接
2. 基于TCP協(xié)議之上的高層協(xié)議,無需開發(fā)者關(guān)心通訊細(xì)節(jié)
3. 提供了高度抽象的編程接口,業(yè)務(wù)開發(fā)成本較低
WebSocket協(xié)議的交互流程

客戶端首先發(fā)起一個(gè)Http請(qǐng)求到服務(wù)端,請(qǐng)求的特殊之處,在于在請(qǐng)求里面帶了一個(gè)upgrade的字段,告訴服務(wù)端,我想生成一個(gè)websocket的協(xié)議,服務(wù)端收到請(qǐng)求后,會(huì)給客戶端一個(gè)握手的確認(rèn),返回一個(gè)switching, 意思允許客戶端向websocket協(xié)議轉(zhuǎn)換,完成這個(gè)協(xié)商之后,客戶端與服務(wù)端之間的底層TCP協(xié)議是沒有中斷的,接下來,客戶端可以向服務(wù)端發(fā)起一個(gè)基于websocket協(xié)議的消息,服務(wù)端也可以主動(dòng)向客戶端發(fā)起websocket協(xié)議的消息,websocket協(xié)議里面通訊的單位就叫message。
傳輸協(xié)議原理
協(xié)議升級(jí)后,繼續(xù)復(fù)用Http協(xié)議的底層socket完成后續(xù)通訊
message底層會(huì)被切分成多個(gè)frame幀進(jìn)行傳輸,從協(xié)議層面不能傳輸一個(gè)大包,只能切成一個(gè)個(gè)小包傳輸
編程時(shí),只需操作message,無需關(guān)心frame(屬于協(xié)議和類庫自身去操作的)
框架底層完成TCP網(wǎng)絡(luò)I/O,WebSocket協(xié)議的解析,開發(fā)者無需關(guān)心
服務(wù)端技術(shù)選型與考慮
NodeJs:? 單線程模型(盡管可以多進(jìn)程),推送性能有限
C/C++:TCP通訊、WebSocket協(xié)議實(shí)現(xiàn)成本高
Go:? ?多線程,基于協(xié)程模型并發(fā)
? ? ? ?Go語言屬于編譯型語言,運(yùn)行速度并不慢
? ? ? ?成熟的WebSocket標(biāo)準(zhǔn)庫,無需造輪子
基于Go實(shí)現(xiàn)WebSocket服務(wù)端
用Go語言對(duì)WebSocket做一個(gè)簡單的服務(wù)端實(shí)現(xiàn),以及HTML頁面進(jìn)行調(diào)試,并對(duì)WebSocket封裝,這里就直接給出代碼了。
千萬級(jí)彈幕系統(tǒng)的架構(gòu)設(shè)計(jì)
技術(shù)難點(diǎn):
1.內(nèi)核瓶頸
推送量大:100W在線 * 10條/每秒 = 1000W條/秒
內(nèi)核瓶頸:linux內(nèi)核發(fā)送TCP的極限包頻 ≈ 100W/秒
2.鎖瓶頸
需要維護(hù)在線用戶集合(100W用戶在線),通常是一個(gè)字典結(jié)構(gòu)
推送消息即遍歷整個(gè)集合,順序發(fā)送消息,耗時(shí)極長?
推送期間,客戶端仍舊正常的上下線,集合面臨不停的修改,修改需要遍歷,所以集合需要上鎖
3.CPU瓶頸
瀏覽器與服務(wù)端之間一般采用的是JSon格式去通訊
Json編碼非常耗費(fèi)CPU資源
向100W在線推送一次,則需100W次Json Encode
優(yōu)化方案
內(nèi)核瓶頸
減少網(wǎng)絡(luò)小包的發(fā)送,我們將網(wǎng)絡(luò)上幾百字節(jié)定義成網(wǎng)絡(luò)的小包了,小包的問題是對(duì)內(nèi)核和網(wǎng)絡(luò)的中間設(shè)備造成處理的壓力。方案是將一秒內(nèi)N條消息合并成1條消息,合并后,每秒推送數(shù)等于在線連接數(shù)。
鎖瓶頸
大鎖拆小鎖,將長連接打散到多個(gè)集合中去,每個(gè)集合都有自己的鎖,多線程并發(fā)推送集合,線程之間推送的集合不同,所以沒有鎖的競(jìng)爭(zhēng)關(guān)系,避免鎖競(jìng)爭(zhēng)。
讀寫鎖取代互斥鎖,多個(gè)推送任務(wù)可以并發(fā)遍歷相同集合
CPU瓶頸
減少重復(fù)計(jì)算,Json編碼前置,1次消息編碼+100W次推送,消息合并前置,N條消息合并后,只需要編碼一次。
單機(jī)架構(gòu)

最外層是在線的長連接,連接到服務(wù)端后,打散到多個(gè)集合里面存儲(chǔ),我們要發(fā)送的消息呢,通過打包后,經(jīng)過json編碼,被多個(gè)線程或協(xié)程分發(fā)到多個(gè)集合中去,最終推給了所有的在線連接。
單機(jī)瓶頸
維護(hù)海量長連接,會(huì)花費(fèi)不少內(nèi)存
消息推送的瞬時(shí),消耗大量的CPU
消息推送的瞬時(shí)帶寬高達(dá)400-600Mb(4-6Gbits),需要用到萬兆網(wǎng)卡,是主要瓶頸
集群
部署多個(gè)節(jié)點(diǎn),通過負(fù)載均衡,把連接打散到多個(gè) 服務(wù)器上,但推送消息的時(shí)候,不知道哪個(gè)直播間在哪個(gè)節(jié)點(diǎn)上,最常用的方式是將消息廣播給所有的網(wǎng)關(guān)節(jié)點(diǎn),此時(shí)就需要做一個(gè)邏輯集群。
邏輯集群
基于Http2協(xié)議向gateway集群分發(fā)消息(Http2支持連接復(fù)用,用作RPC性能更佳,即在單個(gè)連接上可以做高吞吐的請(qǐng)求應(yīng)答處理)
基于Http1協(xié)議對(duì)外提供推送API(Http1更加普及,對(duì)業(yè)務(wù)方更加友好)
整體分布式架構(gòu)圖如下:

任何業(yè)務(wù)方通過Http接口調(diào)用到邏輯集群,邏輯集群把消息廣播給所有網(wǎng)關(guān),各個(gè)網(wǎng)關(guān)各自將消息推送給在線的連接即可。
本文講解了開發(fā)消息推送服務(wù)的難點(diǎn)與解決方案的大體思路,按照整個(gè)理論流程下來,基本能實(shí)現(xiàn)一套彈幕消息推送的服務(wù)。
轉(zhuǎn)自:https://blog.csdn.net/Wing_93/article/details/81587809