- 架構(gòu)與特性:一個(gè)完整的IM系統(tǒng)是怎樣的照藻?
- 輪詢與長(zhǎng)連接:如何解決消息的實(shí)時(shí)到達(dá)問題?(實(shí)時(shí)性)
- ACK機(jī)制:如何保證消息的可靠投遞茫蛹?(可靠性)
- 消息序號(hào)生成器:如何保證你的消息不會(huì)亂序?(一致性)
- HttpDNS和TLS:你的消息聊天真的安全嗎梯嗽?(安全性)
- 分布式鎖和原子性:你看到的未讀消息提醒是真的嗎?
- 智能心跳機(jī)制:解決網(wǎng)絡(luò)的不確定性
- HTTP Tunnel:復(fù)雜網(wǎng)絡(luò)下消息通道高可用設(shè)計(jì)的思考
- 分片上傳:如何讓你的圖片锌介、音視頻消息發(fā)送得更快秋麸?
- CDN加速:如何讓你的圖片渐排、視頻、語(yǔ)音消息瀏覽播放不卡灸蟆?
- APNs:聊一聊第三方系統(tǒng)級(jí)消息通道的事
- Cache:多級(jí)緩存架構(gòu)在消息系統(tǒng)中的應(yīng)用
架構(gòu)與特性:一個(gè)完整的IM系統(tǒng)是怎樣的驯耻?
即時(shí)消息有別于其他業(yè)務(wù)系統(tǒng)的四大特性:
實(shí)時(shí)性:保證消息實(shí)時(shí)觸達(dá)是互動(dòng)場(chǎng)景的必備能力
可靠性:“不丟消息”和“消息不重復(fù)”是系統(tǒng)值得信賴的前置條件
一致性:“多用戶”“多終端”的一致性體驗(yàn)?zāi)艽蠓嵘?IM 系統(tǒng)的使用體驗(yàn)
安全性:“數(shù)據(jù)傳輸安全”“數(shù)據(jù)存儲(chǔ)安全”“消息內(nèi)容安全“三大保障方面提供全面隱私保護(hù)
輪詢與長(zhǎng)連接:如何解決消息的實(shí)時(shí)到達(dá)問題?(實(shí)時(shí)性)
解決“消息實(shí)時(shí)性”經(jīng)歷過的幾個(gè)代表性階段:
短輪詢場(chǎng)景:定期炒考、高頻地輪詢服務(wù)端的新消息可缚,如果有新消息就會(huì)將新消息返回給客戶端,如果沒有新消息就返回空列表斋枢,并關(guān)閉連接
劣勢(shì):
頻率一般較高帘靡,但大部分請(qǐng)求實(shí)際上是無(wú)用的,客戶端既費(fèi)電也費(fèi)流量
對(duì)服務(wù)端資源的壓力也較大瓤帚,一是大量服務(wù)器用于扛高頻輪詢的 QPS(每秒查詢率)描姚,二是對(duì)后端存儲(chǔ)資源也有較大壓力
長(zhǎng)輪詢場(chǎng)景:當(dāng)本次請(qǐng)求沒有獲取到新消息時(shí),并不會(huì)馬上結(jié)束返回戈次,而是會(huì)在服務(wù)端“懸掛(hang)”轩勘,等待一段時(shí)間,如果在等待的這段時(shí)間內(nèi)有新消息產(chǎn)生怯邪,就能馬上響應(yīng)返回(應(yīng)用場(chǎng)景:對(duì)實(shí)時(shí)性要求比較高绊寻,但是整體用戶量不太大)
劣勢(shì):
只是降低了入口請(qǐng)求的 QPS,并沒有減少對(duì)后端資源輪詢的壓力
仍然沒有完全解決客戶端“無(wú)效”請(qǐng)求的問題
由于短輪詢和長(zhǎng)輪詢是基于 HTTP 協(xié)議實(shí)現(xiàn)的悬秉,由于 HTTP 是一個(gè)無(wú)狀態(tài)協(xié)議榛斯,服務(wù)端在有新消息產(chǎn)生時(shí),沒有辦法直接向客戶端進(jìn)行推送搂捧,所以沒法做到基于事件的完全的“邊緣觸發(fā)(當(dāng)狀態(tài)變化時(shí),發(fā)生一個(gè) IO 事件)”
WebSocket:
WebSocket 是一種服務(wù)端推送的技術(shù)代表
隨著 HTML5 的出現(xiàn)懂缕,基于單個(gè) TCP 連接的全雙工通信的協(xié)議 WebSocket 在 2011 年成為 RFC 標(biāo)準(zhǔn)協(xié)議允跑,逐漸代替了短輪詢和長(zhǎng)輪詢的方式
基于 WebSocket 實(shí)現(xiàn)的 IM 服務(wù),客戶端和服務(wù)端只需要完成一次握手搪柑,就可以創(chuàng)建持久的長(zhǎng)連接聋丝,并進(jìn)行隨時(shí)的雙向數(shù)據(jù)傳輸。當(dāng)服務(wù)端接收到新消息時(shí)工碾,可以通過建立的 WebSocket 連接弱睦,直接進(jìn)行推送,真正做到“邊緣觸發(fā)”渊额,也保證了消息到達(dá)的實(shí)時(shí)性
優(yōu)勢(shì):
支持服務(wù)端推送的雙向通信况木,大幅降低服務(wù)端輪詢壓力
數(shù)據(jù)交互的控制開銷低垒拢,降低雙方通信的網(wǎng)絡(luò)開銷
Web 原生支持,實(shí)現(xiàn)相對(duì)簡(jiǎn)單
TCP 長(zhǎng)連接衍生的 IM 協(xié)議:
除了 WebSocket 協(xié)議火惊,還有其他一些常用的基于 TCP 長(zhǎng)連接衍生的通信協(xié)議求类,如 XMPP 協(xié)議、MQTT 協(xié)議以及各種私有協(xié)議
XMPP: XMPP協(xié)議雖然比較成熟屹耐、擴(kuò)展性也不錯(cuò)尸疆,但基于 XML 格式的協(xié)議傳輸上冗余比較多,在流量方面不太友好惶岭,而且整體實(shí)現(xiàn)上比較復(fù)雜寿弱,在如今移動(dòng)網(wǎng)絡(luò)場(chǎng)景下用的并不多
MQTT:輕量級(jí)的 MQTT 基于代理的“發(fā)布 / 訂閱”模式,在省流量和擴(kuò)展性方面都比較突出按灶,在很多消息推送場(chǎng)景下被廣泛使用症革,但這個(gè)協(xié)議并不是 IM 領(lǐng)域的專有協(xié)議,因此對(duì)于很多 IM 下的個(gè)性化業(yè)務(wù)場(chǎng)景仍然需要大量復(fù)雜的擴(kuò)展和開發(fā)兆衅,比如不支持群組功能地沮、不支持離線消息
對(duì)于開發(fā)人力相對(duì)充足的大廠,目前很多是基于 TCP(或者 UDP)來(lái)實(shí)現(xiàn)自己的私有協(xié)議羡亩,一方面私有協(xié)議能夠貼合業(yè)務(wù)需要摩疑,做到真正的高效和省流;另一方面私有協(xié)議相對(duì)安全性更高一些畏铆,被破解的可能性小
ACK機(jī)制:如何保證消息的可靠投遞雷袋?(可靠性)
可靠投遞主要是指:消息在發(fā)送接收過程中能夠做到不丟消息、消息不重復(fù)兩點(diǎn)
不丟消息:
發(fā)消息大概整體上分為兩部分:
第一部分:
用戶 A 發(fā)送消息到 IM 服務(wù)器辞居,服務(wù)器將消息暫存楷怒,然后返回成功的結(jié)果給發(fā)送方 A(步驟 1、2瓦灶、3)
丟失消息情況:
用戶 A 在把消息發(fā)送到 IM 服務(wù)器的過程中鸠删,由于網(wǎng)絡(luò)不通等原因失敗了
IM 服務(wù)器接收到消息進(jìn)行服務(wù)端存儲(chǔ)時(shí)失敗了
用戶 A 等待 IM 服務(wù)器一定的超時(shí)時(shí)間,但 IM 服務(wù)器一直沒有返回結(jié)果
解決方案:
通過客戶端 A 的超時(shí)重發(fā)和 IM 服務(wù)器的去重機(jī)制贼陶,基本就可以解決問題
第二部分:
IM 服務(wù)器接著再將暫存的用戶 A 發(fā)出的消息刃泡,推送給接收方用戶 B(步驟 4)
丟失消息情況:
服務(wù)端出現(xiàn)掉電,導(dǎo)致消息不能成功推送給用戶 B
用戶 B 的設(shè)備在接收后的處理過程出現(xiàn)問題碉怔,也會(huì)導(dǎo)致消息丟失
解決方案:
業(yè)界一般參考 TCP 協(xié)議的 ACK 機(jī)制烘贴,實(shí)現(xiàn)一套業(yè)務(wù)層的 ACK 協(xié)議,通過“ACK+ 超時(shí)重傳 + 去重”的組合機(jī)制撮胧,能解決大部分用戶在線時(shí)消息推送丟失的問題
ps:假設(shè)一臺(tái) IM 服務(wù)器在推送出消息后桨踪,由于硬件原因宕機(jī)了,這種情況下芹啥,如果這條消息真的丟了锻离,由于負(fù)責(zé)的 IM 服務(wù)器宕機(jī)了無(wú)法觸發(fā)重傳铺峭,導(dǎo)致接收方 B 收不到這條消息。當(dāng)用戶 B 再次重連上線后纳账,可能并不知道之前有一條消息丟失的情況
解決方案:
通過“兜底”的完整性檢查機(jī)制來(lái)及時(shí)發(fā)現(xiàn)消息丟失的情況并進(jìn)行補(bǔ)推修復(fù)逛薇,消息完整性檢查可以通過時(shí)間戳比對(duì),或者全局自增序列等方式來(lái)實(shí)現(xiàn)
消息序號(hào)生成器:如何保證你的消息不會(huì)亂序疏虫?(一致性)
更多的場(chǎng)景下永罚,我們可能需要面對(duì)的是多發(fā)送方、多接收方卧秘、服務(wù)端多線程并發(fā)處理的情況呢袱,所以保證消息的時(shí)序一致比較困難。
保證消息的時(shí)序一致性的一個(gè)關(guān)鍵問題是:我們是否能找到這么一個(gè)時(shí)序基準(zhǔn)翅敌,使得我們的消息具備“時(shí)序可比較性”
如何找到時(shí)序基準(zhǔn)羞福?
發(fā)送方的本地序號(hào)和本地時(shí)鐘作為“時(shí)序基準(zhǔn)”,不適合
IM 服務(wù)器的本地時(shí)鐘作為“時(shí)序基準(zhǔn)”蚯涮,不適合
可以通過全局的序號(hào)生成器來(lái)確定治专。常見的實(shí)現(xiàn)方式包括支持單調(diào)自增序號(hào)的資源生成,或者分布式時(shí)間相關(guān)的 ID 生成服務(wù)生成遭顶。兩種方式各有一些限制张峰,不過,你都可以根據(jù)業(yè)務(wù)自身的特征來(lái)進(jìn)行選擇棒旗。
有了通過時(shí)序基準(zhǔn)確定的消息序號(hào)喘批,由于 IM 服務(wù)器差異和多線程處理的方式,不能保證服務(wù)端的消息一定能按順序推到接收方铣揉。我們可以通過“服務(wù)端包內(nèi)整流”機(jī)制來(lái)保證需要“嚴(yán)格有序”批量消息的正確執(zhí)行饶深;或者,接收方根據(jù)消息序號(hào)來(lái)進(jìn)行消息本地整流逛拱,從而確保多接收方的最終一致性敌厘。
HttpDNS和TLS:你的消息聊天真的安全嗎?(安全性)
消息安全性的三個(gè)維度:
消息傳輸安全性朽合、消息存儲(chǔ)安全性俱两、消息內(nèi)容安全性
消息傳輸安全性:
在消息傳輸過程中主要關(guān)注兩個(gè)問題:“訪問入口安全”和“傳輸鏈路安全”,這也是兩個(gè)基于互聯(lián)網(wǎng)的即時(shí)消息場(chǎng)景下的重要防范點(diǎn)
訪問入口安全
常見的問題就是 DNS 劫持:
1.路由器的 DNS 設(shè)置被非法侵入篡改了
這種問題常見于一些家用寬帶路由器旁舰,由于安全性設(shè)置不夠(比如使用默認(rèn)密碼),導(dǎo)致路由器被黑客或者木馬修改了嗡官,DNS 設(shè)置為惡意的 DNS 地址箭窜,這些有問題的 DNS 服務(wù)器會(huì)在你訪問某些網(wǎng)站時(shí)返回仿冒內(nèi)容,或者植入彈窗廣告等
解決方案:一般會(huì)重置一下路由器的配置衍腥,然后修改默認(rèn)的路由管理登錄密碼磺樱,基本上都能解決
2.運(yùn)營(yíng)商 LocalDNS 可能會(huì)導(dǎo)致接入域名的解析被劫持
①LocalDNS 是部分運(yùn)營(yíng)商為了降低跨網(wǎng)流量纳猫,緩存部分域名的指向內(nèi)容,把域名強(qiáng)行指向自己的內(nèi)容緩存服務(wù)器的 IP 地址
②運(yùn)營(yíng)商可能會(huì)修改 DNS 的 TTL(Time-To-Live竹捉,DNS 緩存時(shí)間)芜辕,導(dǎo)致 DNS 的變更生效延遲,影響服務(wù)可用性块差。我們之前一個(gè)線上業(yè)務(wù)域名的 TTL 在某些省市能達(dá)到 24 小時(shí)
③一些小運(yùn)營(yíng)商為了減輕自身的資源壓力侵续,把 DNS 請(qǐng)求轉(zhuǎn)發(fā)給其他運(yùn)營(yíng)商去解析,這樣分配的 IP 地址可能存在跨運(yùn)營(yíng)商訪問的問題憨闰,導(dǎo)致請(qǐng)求變慢甚至不可用
解決方案:HttpDNS
HttpDNS 繞開了運(yùn)營(yíng)商的 LocalDNS状蜗,通過 HTTP 協(xié)議(而不是基于 UDP 的 DNS 標(biāo)準(zhǔn)協(xié)議)來(lái)直接和 DNS 服務(wù)器交互,能有效防止域名被運(yùn)營(yíng)商劫持的問題
由于 HttpDNS 服務(wù)器能獲取到真實(shí)的用戶出口 IP鹉动,所以能選擇離用戶更近的節(jié)點(diǎn)進(jìn)行接入轧坎,或者一次返回多個(gè)接入 IP,讓客戶端通過測(cè)速等方式選擇速度更快的接入 IP泽示,因此整體上接入調(diào)度也更精準(zhǔn)
傳輸鏈路安全
消息在傳輸鏈路中的安全隱患:
1.中斷缸血,攻擊者破壞或者切斷網(wǎng)絡(luò),破壞服務(wù)可用性
解決方案:可以采取多通道方式來(lái)提升鏈路可用性械筛,比如很多 IM 系統(tǒng)的實(shí)現(xiàn)中捎泻,如果主鏈路連接不通或者連接不穩(wěn)定,就會(huì)嘗試自動(dòng)切換到 failover 通道变姨,這個(gè) failover 通道可以是:
①?gòu)?HttpDNS 服務(wù)返回的多個(gè)“接入 IP”中選擇性進(jìn)行切換
②從當(dāng)前數(shù)據(jù)傳輸協(xié)議切換到其他傳輸協(xié)議
2.截獲族扰,攻擊者非法竊取傳輸?shù)南?nèi)容,屬于被動(dòng)攻擊
3.篡改定欧,攻擊者非法篡改傳輸?shù)南?nèi)容渔呵,破壞消息完整性和真實(shí)語(yǔ)義
4.偽造,攻擊者偽造正常的通訊消息來(lái)模擬正常用戶或者模擬 IM 服務(wù)端
解決方案:利用私有協(xié)議和 TLS 的技術(shù)來(lái)進(jìn)行防控
私有協(xié)議:采用二進(jìn)制私有協(xié)議的即時(shí)消息系統(tǒng)本身由于編碼問題天然具備一定的防竊取和防篡改的能力
TLS:TLS 巧妙地把“對(duì)稱加密算法”“非對(duì)稱加密算法”“秘鑰交換算法”“消息認(rèn)證碼算法”“數(shù)字簽名證書”“CA 認(rèn)證”進(jìn)行結(jié)合砍鸠,有效地解決了消息傳輸過程中的截獲扩氢、篡改、偽造問題
實(shí)現(xiàn)過程:
1.非對(duì)稱加密算法和秘鑰交換算法用于保證消息加密的密鑰不被破解和泄露
2.對(duì)稱加密算法對(duì)消息進(jìn)行加密爷辱,保證業(yè)務(wù)數(shù)據(jù)傳輸過程被截獲后無(wú)法破解录豺,也無(wú)法篡改消息
3.數(shù)字簽名和 CA 認(rèn)證能驗(yàn)證證書持有者的公鑰有效性,防止服務(wù)端身份的偽造
消息存儲(chǔ)安全性:
賬號(hào)密碼存儲(chǔ)安全:“單向散列”算法:
針對(duì)賬號(hào)密碼的存儲(chǔ)安全一般比較多的采用“高強(qiáng)度單向散列算法”(比如:SHA饭弓、MD5 算法)和每個(gè)賬號(hào)獨(dú)享的“鹽”(這里的“鹽”是一個(gè)很長(zhǎng)的隨機(jī)字符串)結(jié)合來(lái)對(duì)密碼原文進(jìn)行加密存儲(chǔ)
消息內(nèi)容存儲(chǔ)安全:端到端加密:
1.消息內(nèi)容采用“端到端加密”(E2EE)双饥,中間任何鏈路環(huán)節(jié)都不對(duì)消息進(jìn)行解密(國(guó)內(nèi)的大部分即時(shí)消息軟件如 QQ、微信等由于網(wǎng)絡(luò)安全要求弟断,目前暫時(shí)還沒有采用“端到端加密”)
2.消息內(nèi)容不在服務(wù)端存儲(chǔ)
消息內(nèi)容安全性:
內(nèi)容安全性主要是指針對(duì)消息內(nèi)容的識(shí)別和傳播的控制咏花,一般都依托于第三方的內(nèi)容識(shí)別服務(wù)來(lái)進(jìn)行”風(fēng)險(xiǎn)內(nèi)容“的防范
1.建立敏感詞庫(kù),針對(duì)文字內(nèi)容進(jìn)行安全識(shí)別
2.依托圖片識(shí)別技術(shù)來(lái)對(duì)色情圖片和視頻、廣告圖片昏翰、涉政圖片等進(jìn)行識(shí)別處置
3.使用“語(yǔ)音轉(zhuǎn)文字”和 OCR(圖片文本識(shí)別)來(lái)輔助對(duì)圖片和語(yǔ)音的進(jìn)一步挖掘識(shí)別
4.通過爬蟲技術(shù)來(lái)對(duì)鏈接內(nèi)容進(jìn)行進(jìn)一步分析苍匆,識(shí)別“風(fēng)險(xiǎn)外鏈”
并且可以配合“聯(lián)動(dòng)懲罰處置”來(lái)進(jìn)行風(fēng)險(xiǎn)識(shí)別的后置閉環(huán)
分布式鎖和原子性:你看到的未讀消息提醒是真的嗎?
會(huì)話未讀和總未讀單獨(dú)維護(hù):
“總未讀”在很多業(yè)務(wù)場(chǎng)景里會(huì)被高頻使用棚菊,比如每次消息推送需要把總未讀帶上用于角標(biāo)未讀展示浸踩。如果每次都通過聚合所有會(huì)話未讀來(lái)獲取,一旦用戶的互動(dòng)會(huì)話數(shù)比較多统求,由于需要多次從存儲(chǔ)獲取检碗,容易出現(xiàn)某些會(huì)話未讀由于超時(shí)等原因沒取到,導(dǎo)致總未讀數(shù)計(jì)算少了球订,所以許多 App 內(nèi)會(huì)通過定時(shí)輪詢的方式來(lái)同步客戶端和服務(wù)端的總未讀數(shù)
為什么會(huì)造成會(huì)話未讀和總未讀不一致后裸?
兩個(gè)未讀的變更不是原子性的,會(huì)出現(xiàn)某一個(gè)成功另一個(gè)失敗的情況冒滩,也會(huì)出現(xiàn)由于并發(fā)更新導(dǎo)致操作被覆蓋的情況微驶。所以要解決這些問題,需要保證兩個(gè)未讀更新操作的原子性
分布式鎖
可以依賴 DB 的唯一性开睡、約束來(lái)通過某一條固定記錄的插入成功與否因苹,來(lái)判斷鎖的獲取,也可以通過一些分布式緩存來(lái)實(shí)現(xiàn)
具備較好普適性篇恒,但執(zhí)行效率較差扶檐,鎖的管理也比較復(fù)雜,適用于較小規(guī)模的即時(shí)消息場(chǎng)景胁艰;
支持事務(wù)功能的資源
事務(wù)提供了一種“將多個(gè)命令打包款筑, 然后一次性按順序地執(zhí)行”的機(jī)制, 并且事務(wù)在執(zhí)行的期間不會(huì)主動(dòng)中斷腾么,服務(wù)器在執(zhí)行完事務(wù)中的所有命令之后奈梳,才會(huì)繼續(xù)處理其他客戶端的其他命令
不需要額外的維護(hù)鎖的資源,實(shí)現(xiàn)較為簡(jiǎn)單解虱,但基于樂觀鎖的 watch 機(jī)制在較高并發(fā)場(chǎng)景下失敗率較高攘须,執(zhí)行效率比較容易出現(xiàn)瓶頸
智能心跳機(jī)制:解決網(wǎng)絡(luò)的不確定性
為了保證“可靠投遞”和“實(shí)時(shí)性,大部分 IM 系統(tǒng)會(huì)通過“長(zhǎng)連接”的方式來(lái)建立收發(fā)雙方的通信通道殴泰,這些基于 TCP 長(zhǎng)連接的通信協(xié)議于宙,在用戶上線連接時(shí),會(huì)在服務(wù)端維護(hù)好連接到服務(wù)器的用戶設(shè)備和具體 TCP 連接的映射關(guān)系悍汛,通過這種方式服務(wù)端也能通過這個(gè)映射關(guān)系隨時(shí)找到對(duì)應(yīng)在線的用戶的客戶端捞魁,通過“服務(wù)端推送”,提供更加實(shí)時(shí)的消息下發(fā)离咐,而且這個(gè)長(zhǎng)連接一旦建立就一直存在谱俭,除非網(wǎng)絡(luò)被中斷
相對(duì)于短連接方式也能省略 TCP 握手和 TLS 握手的幾個(gè) RTT 的時(shí)間開銷(節(jié)約不必要的資源開銷),在用戶體驗(yàn)和實(shí)時(shí)性上也會(huì)更好
“長(zhǎng)連接”底層使用的 TCP 連接并不是一個(gè)真正存在的物理連接,實(shí)際上只是一個(gè)無(wú)感知的虛擬連接旺上,中間鏈路的斷開連接的兩端不會(huì)感知到,因此維護(hù)好這個(gè)“長(zhǎng)連接”一個(gè)關(guān)鍵的問題在于能夠讓這個(gè)“長(zhǎng)連接”能夠在中間鏈路出現(xiàn)問題時(shí)糖埋,讓連接的兩端能快速得到通知宣吱,然后通過“重連”來(lái)重新建立新的可用連接,從而讓我們這個(gè)“長(zhǎng)連接”一直保持“高可用”狀態(tài)瞳别,這個(gè)“快速”“不間斷”識(shí)別連接可用性的機(jī)制征候,被稱為“心跳機(jī)制”
心跳機(jī)制在長(zhǎng)連接維護(hù)中的必要性:
- 降低服務(wù)端連接維護(hù)無(wú)效連接的開銷。
- 支持客戶端快速識(shí)別無(wú)效連接祟敛,自動(dòng)斷線重連疤坝。
- 連接保活馆铁,避免被運(yùn)營(yíng)商 NAT 超時(shí)斷開跑揉。
心跳檢測(cè)的幾種實(shí)現(xiàn)方式:
TCP Keepalive
TCP 的 Keepalive 作為操作系統(tǒng)的 TCP/IP 協(xié)議棧實(shí)現(xiàn)的一部分,對(duì)于本機(jī)的 TCP 連接埠巨,會(huì)在連接空閑期按一定的頻次历谍,自動(dòng)發(fā)送不攜帶數(shù)據(jù)的探測(cè)報(bào)文,來(lái)探測(cè)對(duì)方是否存活辣垒。操作系統(tǒng)默認(rèn)是關(guān)閉這個(gè)特性的望侈,需要由應(yīng)用層來(lái)開啟
操作系統(tǒng) TCP/IP 協(xié)議棧自帶,無(wú)需二次開發(fā)勋桶,使用簡(jiǎn)單脱衙,不攜帶數(shù)據(jù)網(wǎng)絡(luò)流量消耗少。但存在靈活性不夠(一臺(tái)服務(wù)器某一時(shí)間只能調(diào)整為固定間隔的心跳)和無(wú)法判斷應(yīng)用層是否可用(雖然能夠用于連接層存活的探測(cè)例驹,但并不代表真正的應(yīng)用層處于可用狀態(tài))的缺陷
應(yīng)用層心跳
使用應(yīng)用層心跳來(lái)提升探測(cè)的靈活性和準(zhǔn)確性捐韩。應(yīng)用層心跳實(shí)際上就是客戶端每隔一定時(shí)間間隔,向 IM 服務(wù)端發(fā)送一個(gè)業(yè)務(wù)層的數(shù)據(jù)包告知自身存活
應(yīng)用自己實(shí)現(xiàn)心跳機(jī)制眠饮,需要一定的代碼開發(fā)量奥帘,網(wǎng)絡(luò)流量消耗稍微多一點(diǎn),但由于需要在應(yīng)用層進(jìn)行發(fā)送和接收的處理仪召,因此更能反映應(yīng)用的可用性寨蹋,而不是僅僅代表網(wǎng)絡(luò)可用,且心跳間隔的靈活性好(可以根據(jù)實(shí)際網(wǎng)絡(luò)的情況扔茅,來(lái)靈活設(shè)置心跳間隔已旧,按照固定頻率發(fā)送心跳包或者客戶端在發(fā)送數(shù)據(jù)空閑后才發(fā)送心跳包),配合智能心跳機(jī)制召娜,可以做到“保證 NAT 不超時(shí)的情況下最大化節(jié)約設(shè)備資源消耗”运褪,同時(shí)也能更精確反饋應(yīng)用層的真實(shí)可用性
HTTP Tunnel:復(fù)雜網(wǎng)絡(luò)下消息通道高可用設(shè)計(jì)的思考
對(duì)于即時(shí)消息系統(tǒng)來(lái)說(shuō),消息的通道主要承載兩部分流量:一部分是用戶發(fā)出的消息或者觸發(fā)的行為,我們稱為上行消息秸讹;一部分是服務(wù)端主動(dòng)下推的消息和信令檀咙,我們稱為下行消息,我們需要保障消息通道的高可用璃诀。
在解決“通道連不上”的問題上:
多端口訪問:
計(jì)算機(jī)端口范圍是 0 ~ 65535弧可,主要分成三大類:公認(rèn)端口(0 ~ 1023)、注冊(cè)端口(1024 ~ 49151)劣欢、動(dòng)態(tài)或私有端口(49152 ~ 65535)棕诵,業(yè)界確認(rèn)比較安全的端口基本上只有 80、8080凿将、443校套、14000 這幾個(gè),因此如果開發(fā)一個(gè)外網(wǎng)服務(wù)牧抵,我們應(yīng)當(dāng)盡量選用這幾個(gè)端口來(lái)對(duì)外進(jìn)行暴露笛匙,還可以通過同時(shí)暴露這幾個(gè)端口中的某幾個(gè),來(lái)進(jìn)一步提升可連通性
HTTP Tunnel:
解決某些網(wǎng)絡(luò)情況下只允許 HTTP 協(xié)議的數(shù)據(jù)傳輸?shù)膯栴}犀变,通過 HTTP Tunnel 的方式來(lái)對(duì)網(wǎng)絡(luò)代理進(jìn)行穿透膳算,其實(shí)就是通過 HTTP 協(xié)議,來(lái)封裝其他由于網(wǎng)絡(luò)原因不兼容的協(xié)議(比如 TCP 私有協(xié)議)
多接入點(diǎn) IP 列表:
通過 HttpDNS(不僅能解決 DNS 劫持的問題弛作,還能通過返回多個(gè)接入點(diǎn) IP 來(lái)解決連通性的問題) 和客戶端預(yù)埋的方式涕蜂,提供多個(gè)可選的通道接入點(diǎn),讓某些接入點(diǎn)在連不上時(shí)還能嘗試其他接入點(diǎn)
在解決“通道連接慢”的問題上:
解決跨網(wǎng)延遲:
以通過支持多運(yùn)營(yíng)商機(jī)房接入點(diǎn)映琳,來(lái)避免用戶的跨運(yùn)營(yíng)商網(wǎng)絡(luò)訪問
跑馬競(jìng)速:
對(duì)于提供的多接入點(diǎn)机隙,客戶端還可以通過“跑馬競(jìng)速”的方式優(yōu)先使用連接速度更快的接入點(diǎn)來(lái)訪問,所謂的“跑馬競(jìng)速”萨西,你可以理解為類似賽馬一樣有鹿,我們一次放出多匹馬參與比賽,最終跑得最快的馬勝出
在解決“通道不穩(wěn)定”的問題上:
通道和業(yè)務(wù)解耦:
我們主要從服務(wù)端的架構(gòu)設(shè)計(jì)著手谎脯,讓我們的通道層服務(wù)和變化頻繁的業(yè)務(wù)進(jìn)行解耦葱跋,避免業(yè)務(wù)頻繁變動(dòng)導(dǎo)致通道層服務(wù)不穩(wěn)定
上下行通道隔離:
對(duì)于消息下行通道壓力大的業(yè)務(wù)場(chǎng)景,還可以隔離消息上下行通道源梭,避免消息的上行被壓力大的下行通道所影響
獨(dú)立多媒體上傳下載:
另外娱俺,將多媒體的上傳下載通道和消息收發(fā)的核心通道進(jìn)行隔離,避免傳輸量大的多媒體消息造成通道的阻塞废麻,影響消息收發(fā)
分片上傳:如何讓你的圖片荠卷、音視頻消息發(fā)送得更快?
從 IM 中圖片烛愧、語(yǔ)音油宜、視頻等多媒體消息的發(fā)送場(chǎng)景的優(yōu)化出發(fā)掂碱,分析了目前業(yè)界比較常用的一些優(yōu)化手段
分片上傳:
分片上傳是指在客戶端把文件按照一定規(guī)則,分成多個(gè)數(shù)據(jù)塊并標(biāo)記序號(hào)慎冤,利用“并行”的方式來(lái)同時(shí)上傳多個(gè)分片疼燥,服務(wù)端按照序號(hào)重新將多個(gè)數(shù)據(jù)塊組裝成文件
斷點(diǎn)續(xù)傳:
分片上傳機(jī)制,就能相對(duì)簡(jiǎn)單地實(shí)現(xiàn)“斷點(diǎn)續(xù)傳”功能蚁堤,給每一次上傳行為分配一個(gè)唯一的操作標(biāo)識(shí)悴了,每個(gè)分片在上傳時(shí)除了攜帶自己的序號(hào)外,還需要帶上這個(gè)操作標(biāo)識(shí)违寿,服務(wù)端針對(duì)接收到的同一個(gè)操作標(biāo)識(shí)的分片進(jìn)行“暫存”,即使由于某個(gè)原因暫停上傳了熟空,這些“暫存”的分片也不會(huì)馬上清理掉藤巢,而是保留一定的時(shí)間,續(xù)傳時(shí)繼續(xù)以之前同一個(gè)操作標(biāo)識(shí)來(lái)上傳息罗,客戶端先檢查服務(wù)端已有分片的情況掂咒,如果沒有過期就繼續(xù)從上次的位置續(xù)傳,否則需要重新從頭開始上傳
秒傳機(jī)制:
客戶端針對(duì)要上傳的文件計(jì)算出一個(gè)特征值(特征值一般是一段較短的字符串迈喉,不同文件的特征值也不一樣)绍刮,真正上傳前先將這個(gè)特征值提交到服務(wù)端,服務(wù)端檢索本地已有的所有文件的特征值挨摸,如果發(fā)現(xiàn)有相同特征值的記錄孩革,就認(rèn)定本次上傳的文件已存在,后續(xù)就可以返回給客戶端已存在文件的相關(guān)信息得运,客戶端本次上傳完成
特征值的計(jì)算一般采用“單向 Hash 算法”來(lái)完成膝蜈,如 MD5 算法、SHA-1 算法熔掺。但這些算法都存在“碰撞”問題饱搏,也就是會(huì)有極低概率出現(xiàn)“不同文件的特征值一樣的情況”。所以可以使用多種單向 Hash 算法置逻,在都一致的情況下才判斷為重復(fù)
CDN加速:如何讓你的圖片推沸、視頻、語(yǔ)音消息瀏覽播放不卡券坞?
從提升用戶圖片瀏覽及音視頻播放體驗(yàn)的角度出發(fā)鬓催,介紹了一些在即時(shí)消息場(chǎng)景中,業(yè)界比較通用的優(yōu)化策略
CDN 加速:
CDN(Content Delivery Network恨锚,內(nèi)容分發(fā)網(wǎng)絡(luò))加速技術(shù)深浮,是將客戶端上傳的圖片、音視頻發(fā)布到多個(gè)分布在各地的 CDN 節(jié)點(diǎn)的服務(wù)器上眠冈,當(dāng)有用戶需要訪問這些圖片和音視頻時(shí)飞苇,能夠通過 DNS 負(fù)載均衡技術(shù)逮矛,根據(jù)用戶來(lái)源就近訪問 CDN 節(jié)點(diǎn)中緩存的圖片和音視頻消息,如果 CDN 節(jié)點(diǎn)中沒有需要的資源验懊,會(huì)先從源站同步到當(dāng)前節(jié)點(diǎn)上卧惜,再返回給用戶。這種優(yōu)化手段可以讓用戶和資源實(shí)現(xiàn)物理位置上的相鄰忿等,以此降低遠(yuǎn)程訪問的耗時(shí)栖忠,提升下載性能
邊下邊播:
在播放器下載完視頻的格式信息、關(guān)鍵幀等信息后贸街,播放器其實(shí)就可以開始進(jìn)入播放庵寞,同時(shí)結(jié)合 HTTP 協(xié)議自帶支持的 Range 頭按需分片獲取后續(xù)的視頻流,從而來(lái)實(shí)現(xiàn)邊下邊播
支持邊下邊播需要有兩個(gè)前提條件:
1.格式信息和關(guān)鍵幀信息在文件流的頭部
2.服務(wù)端支持 Range 分片獲取
H.265 轉(zhuǎn)碼:
視頻的碼率是數(shù)據(jù)傳輸時(shí)單位時(shí)間傳送的數(shù)據(jù) BPS薛匪。同一種編碼格式下捐川,碼率越高,視頻越清晰逸尖;反之碼率太低古沥,視頻清晰度不夠,用戶體驗(yàn)會(huì)下降娇跟。但碼率太高岩齿,帶寬成本和下載流量也相應(yīng)會(huì)增加
主流的視頻格式采用 H.264 編碼,H.265(又名 HEVC)是 2013 年新制定的視頻編碼標(biāo)準(zhǔn)苞俘。同樣的畫質(zhì)和同樣的碼率盹沈,H.265 比 H.264 占用的存儲(chǔ)空間要少 50%,但 H.265 的編碼復(fù)雜度遠(yuǎn)高于 H.264(10 倍左右)吃谣。針對(duì)熱門的小視頻采用 H.265 轉(zhuǎn)碼襟诸,在保證畫質(zhì)的同時(shí),降低帶寬成本并加快視頻加載
預(yù)加載:
可以對(duì)視頻流進(jìn)行“部分提前加載”基协,達(dá)到視頻播放“秒開”的效果歌亲,比如 WiFi 場(chǎng)景下,在用戶打開聊天會(huì)話頁(yè)時(shí)澜驮,自動(dòng)觸發(fā)當(dāng)前頁(yè)中的小視頻進(jìn)行預(yù)加載陷揪,為了平衡流量和播放體驗(yàn),一般只需要預(yù)加載部分片段杂穷,后續(xù)如果用戶繼續(xù)觀看悍缠,就可以通過邊下邊播的方式再去請(qǐng)求后面的視頻流
預(yù)加載可以按時(shí)間或者大小來(lái)限制。比如耐量,我們可以設(shè)定預(yù)加載 3s 的視頻流飞蚓,或者設(shè)定預(yù)加載 512KB 的視頻流
APNs:聊一聊第三方系統(tǒng)級(jí)消息通道的事
iOS 端的系統(tǒng)推送服務(wù) APNs:
1.app 向 iOS 系統(tǒng)申請(qǐng)遠(yuǎn)程消息推送權(quán)限
2.iOS 系統(tǒng)向 APNs 服務(wù)器請(qǐng)求手機(jī)端的 deviceToken,并告訴 app
3.app 接收到手機(jī)端的 deviceToken
4.app 將收到的 deviceToken 傳給服務(wù)器端
5.服務(wù)器端產(chǎn)生遠(yuǎn)程消息廊蜒,先經(jīng)過 APNs 服務(wù)器
6.APNs 服務(wù)器將遠(yuǎn)程通知根據(jù) deviceToken 推送給相應(yīng)的手機(jī)
DeviceToken 是固定不變的嗎趴拧?
一般來(lái)說(shuō)溅漾,在同一臺(tái)設(shè)備上,設(shè)備的 DeviceToken 是不會(huì)發(fā)生變化的著榴,除了以下幾種情況:
- iOS 系統(tǒng)升級(jí)后
- APNs 出于安全等原因添履,禁用了這個(gè) DeviceToken
我們的 IM 服務(wù)端可以在每次啟動(dòng) App 時(shí),都去請(qǐng)求 APNs 服務(wù)器進(jìn)行注冊(cè)脑又,來(lái)獲取 DeviceToken暮胧。客戶端在首次獲取到 DeviceToken 之后,會(huì)先緩存到本地问麸,如果下次獲取到 DeviceToken 后往衷,它沒有發(fā)生變化,那么就不需要再調(diào)用 IM 服務(wù)端進(jìn)行更新了
靜默推送严卖?
除了通知欄彈窗的強(qiáng)提醒推送外席舍,APNs 還支持“靜默推送”
靜默推送是 iOS 7 之后推出的一種遠(yuǎn)程系統(tǒng)推送類型,它的特色就是沒有文字彈窗妄田,沒有聲音,也沒有角標(biāo)驮捍,可以在不打擾用戶的情況下疟呐,悄無(wú)聲息地喚醒 App 來(lái)進(jìn)行一些更新操作
APNs 的缺陷:
可靠性低:APNs 并不能保證這條消息能真正推送到用戶設(shè)備上,而且也無(wú)法保障消息不發(fā)生延遲(可能丟消息和延遲高)
離線消息的支持差:用戶的設(shè)備離線或者關(guān)機(jī)時(shí)东且,向你這臺(tái)設(shè)備發(fā)送多條推送時(shí)启具,會(huì)啟動(dòng)它的 QoS(Quality of Service,服務(wù)質(zhì)量)機(jī)制珊泳,只保留給最新的一條消息鲁冯,無(wú)法保障離線消息的存儲(chǔ)。(由于存儲(chǔ)成本方面的考慮色查,APNs 就會(huì)丟掉一部分離線消息)
角標(biāo)累加問題:對(duì)于角標(biāo)的未讀數(shù)薯演,APNs 不支持累計(jì) +1 操作,只支持覆蓋原來(lái)的角標(biāo)未讀數(shù)
Cache:多級(jí)緩存架構(gòu)在消息系統(tǒng)中的應(yīng)用
同樣是 1MB 的數(shù)據(jù)讀取秧了,從磁盤讀取的耗時(shí)比從內(nèi)存讀取的耗時(shí)多近 100 倍跨扮,因此業(yè)界常說(shuō)“處理高并發(fā)的三板斧是緩存、降級(jí)和限流”
緩存的分布式算法:取模求余和一致性哈希
取模求余:使用 ID 對(duì)緩存實(shí)例的數(shù)量進(jìn)行取模求余验毡,ID 哈希后對(duì)緩存節(jié)點(diǎn)取模求余衡创,余數(shù)是多少,就緩存到哪個(gè)節(jié)點(diǎn)上
問題:如果某一個(gè)節(jié)點(diǎn)宕機(jī)或者加入新的節(jié)點(diǎn)晶通,節(jié)點(diǎn)數(shù)量發(fā)生變化后璃氢,Hash 后取模求余的結(jié)果就可能和以前不一樣了。導(dǎo)致加減節(jié)點(diǎn)后狮辽,緩存命中率下降嚴(yán)重
一致性哈希:把全量的緩存空間分成 2 的 32 次方個(gè)區(qū)域一也,這些區(qū)域組合成一個(gè)環(huán)形的存儲(chǔ)結(jié)構(gòu)巢寡;每一個(gè)緩存的 ID 通過哈希算法轉(zhuǎn)化為一個(gè) 32 位的二進(jìn)制數(shù),也就是對(duì)應(yīng)這 2 的 32 次方個(gè)緩存區(qū)域中的某一個(gè)塘秦;緩存的節(jié)點(diǎn)也遵循同樣的哈希算法(比如利用節(jié)點(diǎn)的 IP 來(lái)哈希)讼渊,這些緩存節(jié)點(diǎn)也都能被映射到 2 的 32 次方個(gè)區(qū)域中的某一個(gè)
如何讓 ID 和具體的緩存節(jié)點(diǎn)對(duì)應(yīng)起來(lái)呢?
每一個(gè)映射完的 ID尊剔,按順時(shí)針旋轉(zhuǎn)爪幻,找到離它最近的同樣映射完的緩存節(jié)點(diǎn),該節(jié)點(diǎn)就是 ID 對(duì)應(yīng)的緩存節(jié)點(diǎn)
相對(duì)于取模求余的問題的優(yōu)化:如果某一個(gè)節(jié)點(diǎn)宕機(jī)的話须误,一致性哈希也能保證挨稿,只會(huì)有小部分消息的緩存歸屬節(jié)點(diǎn)發(fā)生變化,大部分仍然能保持不變