拉模式和推模式區(qū)別
拉模式(定時(shí)輪詢?cè)L問(wèn)接口獲取數(shù)據(jù))
1. 數(shù)據(jù)更新頻率低粤剧,則大多數(shù)的數(shù)據(jù)請(qǐng)求時(shí)無(wú)效的
2. 在線用戶數(shù)量多歇竟,則服務(wù)端的查詢負(fù)載很高
3. 定時(shí)輪詢拉取俊扳,無(wú)法滿足時(shí)效性要求
推模式(向客戶端進(jìn)行數(shù)據(jù)的推送)
1. 僅在數(shù)據(jù)更新時(shí)途蒋,才有推送
2. 需要維護(hù)大量的在線長(zhǎng)連接
3. 數(shù)據(jù)更新后猛遍,可以立即推送
基于WebSocket協(xié)議做推送
1. 瀏覽器支持的socket編程馋记,輕松維持服務(wù)端的長(zhǎng)連接
2. 基于TCP協(xié)議之上的高層協(xié)議,無(wú)需開(kāi)發(fā)者關(guān)心通訊細(xì)節(jié)
3. 提供了高度抽象的編程接口懊烤,業(yè)務(wù)開(kāi)發(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é)議是沒(méi)有中斷的,接下來(lái)跛锌,客戶端可以向服務(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瘩欺,無(wú)需關(guān)心frame(屬于協(xié)議和類庫(kù)自身去操作的)
框架底層完成TCP網(wǎng)絡(luò)I/O,WebSocket協(xié)議的解析拌牲,開(kāi)發(fā)者無(wú)需關(guān)心
服務(wù)端技術(shù)選型與考慮
NodeJs:? 單線程模型(盡管可以多進(jìn)程)击碗,推送性能有限
C/C++:TCP通訊、WebSocket協(xié)議實(shí)現(xiàn)成本高
Go:? ?多線程们拙,基于協(xié)程模型并發(fā)
? ? ? ?Go語(yǔ)言屬于編譯型語(yǔ)言稍途,運(yùn)行速度并不慢
? ? ? ?成熟的WebSocket標(biāo)準(zhǔn)庫(kù),無(wú)需造輪子
基于Go實(shí)現(xiàn)WebSocket服務(wù)端
用Go語(yǔ)言對(duì)WebSocket做一個(gè)簡(jiǎn)單的服務(wù)端實(shí)現(xiàn)砚婆,以及HTML頁(yè)面進(jìn)行調(diào)試械拍,并對(duì)WebSocket封裝,這里就直接給出代碼了装盯。
千萬(wàn)級(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í)極長(zhǎng)?
推送期間迄损,客戶端仍舊正常的上下線,集合面臨不停的修改账磺,修改需要遍歷芹敌,所以集合需要上鎖
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ò)的小包了氏捞,小包的問(wèn)題是對(duì)內(nèi)核和網(wǎng)絡(luò)的中間設(shè)備造成處理的壓力。方案是將一秒內(nèi)N條消息合并成1條消息冒版,合并后液茎,每秒推送數(shù)等于在線連接數(shù)。
鎖瓶頸
大鎖拆小鎖辞嗡,將長(zhǎng)連接打散到多個(gè)集合中去捆等,每個(gè)集合都有自己的鎖,多線程并發(fā)推送集合续室,線程之間推送的集合不同栋烤,所以沒(méi)有鎖的競(jìng)爭(zhēng)關(guān)系,避免鎖競(jìng)爭(zhēng)猎贴。
讀寫(xiě)鎖取代互斥鎖班缎,多個(gè)推送任務(wù)可以并發(fā)遍歷相同集合
CPU瓶頸
減少重復(fù)計(jì)算蝴光,Json編碼前置,1次消息編碼+100W次推送达址,消息合并前置蔑祟,N條消息合并后,只需要編碼一次沉唠。
單機(jī)架構(gòu)
最外層是在線的長(zhǎng)連接疆虚,連接到服務(wù)端后,打散到多個(gè)集合里面存儲(chǔ)满葛,我們要發(fā)送的消息呢径簿,通過(guò)打包后,經(jīng)過(guò)json編碼嘀韧,被多個(gè)線程或協(xié)程分發(fā)到多個(gè)集合中去篇亭,最終推給了所有的在線連接。
單機(jī)瓶頸
維護(hù)海量長(zhǎng)連接锄贷,會(huì)花費(fèi)不少內(nèi)存
消息推送的瞬時(shí)译蒂,消耗大量的CPU
消息推送的瞬時(shí)帶寬高達(dá)400-600Mb(4-6Gbits),需要用到萬(wàn)兆網(wǎng)卡谊却,是主要瓶頸
集群
部署多個(gè)節(jié)點(diǎn)柔昼,通過(guò)負(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ù)方通過(guò)Http接口調(diào)用到邏輯集群招盲,邏輯集群把消息廣播給所有網(wǎng)關(guān)低缩,各個(gè)網(wǎng)關(guān)各自將消息推送給在線的連接即可。
本文講解了開(kāi)發(fā)消息推送服務(wù)的難點(diǎn)與解決方案的大體思路曹货,按照整個(gè)理論流程下來(lái)咆繁,基本能實(shí)現(xiàn)一套彈幕消息推送的服務(wù)。
轉(zhuǎn)自:https://blog.csdn.net/Wing_93/article/details/81587809