即時(shí)通訊IM技術(shù)領(lǐng)域提高篇

[TOC]

即時(shí)通訊IM技術(shù)領(lǐng)域提高篇

即時(shí)通訊IM技術(shù)領(lǐng)域基礎(chǔ)篇

接入層的服務(wù)器程序如何升級(jí)

對(duì)于當(dāng)前特定Access長(zhǎng)連接接入服務(wù)而言

我經(jīng)歷的xxx項(xiàng)目中的情況:

  1. Access接入層服務(wù), tcp長(zhǎng)連接的, 如果需要更新的話, 那不是客戶端需要重新登錄 ?

    • 是的,但是可以改造,access 再剝一層出來(lái)專門維護(hù)長(zhǎng)連接
  2. access 分為連接層和 access,前者不涉及業(yè)務(wù),所以預(yù)期不用重啟,后者承載業(yè)務(wù)盛泡,更新重啟對(duì)連接沒(méi)有影響。后面還考慮把 push 合進(jìn) access

  3. 連接層和 access 通過(guò)共享內(nèi)存來(lái)維護(hù)連接信息戈毒。

對(duì)于通用接入層而言

  1. 調(diào)整接入層有狀態(tài)=>無(wú)狀態(tài), 接入層與邏輯層嚴(yán)格分離.

  2. 無(wú)狀態(tài)的接入層,可以隨時(shí)重啟

  3. 邏輯服務(wù)層,可以通過(guò)etcd來(lái)做服務(wù)發(fā)現(xiàn)和注冊(cè),方便升級(jí)和擴(kuò)容

單臺(tái)服務(wù)器維持的TCP長(zhǎng)連接數(shù)

  1. 操作系統(tǒng)包含最大打開文件數(shù)(Max Open Files)限制, 分為系統(tǒng)全局的, 和進(jìn)程級(jí)的限制

    • fs.file-max
    • soft nofile/ hard nofile
  2. 每個(gè)tcp的socket連接都要占用一定內(nèi)存

    • 通過(guò)測(cè)試驗(yàn)證和相關(guān)數(shù)據(jù),只是保持connect,什么也不做,每個(gè)tcp連接,大致占用4k左右的內(nèi)存,百萬(wàn)連接,OS就需要4G以上內(nèi)存.
    • 這里注意還要修改net.ipv4.tcp_rmem/net.ipv4.tcp_wmem
  3. 網(wǎng)絡(luò)限制

    • 假設(shè)百萬(wàn)連接中有 20% 是活躍的, 每個(gè)連接每秒傳輸 1KB 的數(shù)據(jù), 那么需要的網(wǎng)絡(luò)帶寬是 0.2M x 1KB/s x 8 = 1.6Gbps, 要求服務(wù)器至少是萬(wàn)兆網(wǎng)卡(10Gbps).
  4. 一些基本常用的sysctl的修改:

    • net.ipv4.tcp_mem = 78643200 104857600 157286400
    • net.ipv4.tcp_rmem=4096 87380 16777216
    • net.ipv4.tcp_wmem=4096 87380 16777216
    • net.ipv4.ip_local_port_range = 1024 65535
    • net.ipv4.tcp_tw_recycle=1
    • net.ipv4.tcp_tw_reuse=1
    • fs.file-max = 1048576
    • net.ipv4.ip_conntrack_max = 1048576

    n = (mempages * (PAGE_SIZE / 1024)) / 10;

    PAGE_SIZE:typically 4096 in an x86_64

    files_stat.max_files = n;

  5. epoll機(jī)制,長(zhǎng)連接數(shù)太多,會(huì)影響性能嗎? <底層采用紅黑樹和鏈表來(lái)管理數(shù)據(jù)>

    • 這個(gè)不會(huì)影響tcp連接和性能, 哪怕epoll監(jiān)控的事件再多,都OK
    • 內(nèi)核除了幫我們?cè)趀poll文件系統(tǒng)里建了個(gè)file結(jié)點(diǎn)顶吮,在內(nèi)核cache里建了個(gè)紅黑樹用于存儲(chǔ)以后epoll_ctl傳來(lái)的socket外父丰,還會(huì)再建立一個(gè)list鏈表威彰,用于存儲(chǔ)準(zhǔn)備就緒的事件出牧,當(dāng)epoll_wait調(diào)用時(shí),僅僅觀察這個(gè)list鏈表里有沒(méi)有數(shù)據(jù)即可歇盼。
  1. 實(shí)際應(yīng)用中應(yīng)該考慮哪些點(diǎn)呢?

    • 網(wǎng)卡多隊(duì)列的支持, 查看網(wǎng)卡是否支持,要不然cpu不能很好處理網(wǎng)絡(luò)數(shù)據(jù), 這個(gè)需要好的網(wǎng)卡,也消耗cpu

    • 維護(hù)tcp長(zhǎng)連接的節(jié)點(diǎn)管理, 這個(gè)需要消耗cpu, 需要有對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)來(lái)進(jìn)行管理

    • 實(shí)際中,還應(yīng)該考慮,每秒中能夠建立連接的速度,因?yàn)榘偃f(wàn)連接并不是一下就建立的,如果重啟了重連,那么連接速度如何呢 ?

    • 如果這個(gè)節(jié)點(diǎn)掛掉了,請(qǐng)求的分?jǐn)偺幚碓趺磁?

    • 應(yīng)用層對(duì)于每個(gè)連接的處理能力怎樣? 服務(wù)端對(duì)協(xié)議包的解析處理能力如何 ?

    • tcp mem 問(wèn)題,沒(méi)有用到就不會(huì)分配內(nèi)存, 但是不一定會(huì)馬上回收.

  2. 關(guān)于長(zhǎng)連接的另外考慮點(diǎn):

    • 在穩(wěn)定連接情況下舔痕,長(zhǎng)連接數(shù)這個(gè)指標(biāo),在沒(méi)有網(wǎng)絡(luò)吞吐情況下對(duì)比豹缀,其實(shí)意義往往不大伯复,維持連接消耗cpu資源很小,每條連接tcp協(xié)議棧會(huì)占約4k的內(nèi)存開銷邢笙,系統(tǒng)參數(shù)調(diào)整后啸如,我們單機(jī)測(cè)試數(shù)據(jù),最高也是可以達(dá)到單實(shí)例300w長(zhǎng)連接鸣剪。但做更高的測(cè)試组底,我個(gè)人感覺意義不大。

    • 實(shí)際網(wǎng)絡(luò)環(huán)境下筐骇,單實(shí)例300w長(zhǎng)連接债鸡,從理論上算壓力就很大:實(shí)際弱網(wǎng)絡(luò)環(huán)境下,移動(dòng)客戶端的斷線率很高铛纬,假設(shè)每秒有1000分之一的用戶斷線重連厌均。300w長(zhǎng)連接,每秒新建連接達(dá)到3w告唆,這同時(shí)連入的3w用戶棺弊,要進(jìn)行注冊(cè),加載離線存儲(chǔ)等對(duì)內(nèi)rpc調(diào)用擒悬,另外300w長(zhǎng)連接的用戶心跳需要維持,假設(shè)心跳300s一次模她,心跳包每秒需要1w tps。單播和多播數(shù)據(jù)的轉(zhuǎn)發(fā)懂牧,廣播數(shù)據(jù)的轉(zhuǎn)發(fā)侈净,本身也要響應(yīng)內(nèi)部的rpc調(diào)用,300w長(zhǎng)連接情況下僧凤,gc帶來(lái)的壓力畜侦,內(nèi)部接口的響應(yīng)延遲能否穩(wěn)定保障。這些集中在一個(gè)實(shí)例中躯保,可用性是一個(gè)挑戰(zhàn)旋膳。所以線上單實(shí)例不會(huì)hold很高的長(zhǎng)連接,實(shí)際情況也要根據(jù)接入客戶端網(wǎng)絡(luò)狀況來(lái)決定。

  1. 注意的一點(diǎn)就是close_wait 過(guò)多問(wèn)題途事,由于網(wǎng)絡(luò)不穩(wěn)定經(jīng)常會(huì)導(dǎo)致客戶端斷連验懊,如果服務(wù)端沒(méi)有能夠及時(shí)關(guān)閉socket,就會(huì)導(dǎo)致處于close_wait狀態(tài)的鏈路過(guò)多盯孙。
    • close_wait狀態(tài)的鏈路并不釋放句柄和內(nèi)存等資源鲁森,如果積壓過(guò)多可能會(huì)導(dǎo)致系統(tǒng)句柄耗盡,發(fā)生“Too many open files”異常振惰,新的客戶端無(wú)法接入歌溉,涉及創(chuàng)建或者打開句柄的操作都將失敗。
  1. 考慮到不同地區(qū)不同網(wǎng)絡(luò)運(yùn)營(yíng)商的情況下骑晶,用戶可能因?yàn)榫W(wǎng)絡(luò)限制痛垛,連接不上我們的服務(wù)或者比較慢。
    • 我們?cè)趯?shí)踐中就發(fā)現(xiàn)桶蛔,某些網(wǎng)絡(luò)運(yùn)營(yíng)商將某些端口封禁了匙头,導(dǎo)致部分用戶連接不上服務(wù)。為了解決這個(gè)問(wèn)題仔雷,可以提供多個(gè)ip和多個(gè)端口蹂析,客戶端在連接某個(gè)ip比較慢的情況下舔示,可以進(jìn)行輪詢,切換到一個(gè)更快的ip电抚。
  1. TCP_NODELAY
    • 針對(duì)這個(gè)話題惕稻,Thompson認(rèn)為很多在考慮微服務(wù)架構(gòu)的人對(duì)TCP并沒(méi)有充分的理解。在特定的場(chǎng)景中蝙叛,有可能會(huì)遇到延遲的ACK俺祠,它會(huì)限制鏈路上所發(fā)送的數(shù)據(jù)包,每秒鐘只會(huì)有2-5個(gè)數(shù)據(jù)包借帘。這是因?yàn)門CP兩個(gè)算法所引起的死鎖:Nagle以及TCP Delayed Acknowledgement蜘渣。在200-500ms的超時(shí)之后,會(huì)打破這個(gè)死鎖肺然,但是微服務(wù)之間的通信卻會(huì)分別受到影響蔫缸。推薦的方案是使用TCP_NODELAY,它會(huì)禁用Nagle的算法际起,多個(gè)更小的包可以依次發(fā)送捂龄。按照Thompson的說(shuō)法,其中的差別在5到500 req/sec加叁。

    • tcp_nodelay 告訴nginx不要緩存數(shù)據(jù)倦沧,而是一段一段的發(fā)送--當(dāng)需要及時(shí)發(fā)送數(shù)據(jù)時(shí),就應(yīng)該給應(yīng)用設(shè)置這個(gè)屬性它匕,這樣發(fā)送一小塊數(shù)據(jù)信息時(shí)就不能立即得到返回值展融。

    • 我們發(fā)現(xiàn) gRPC 的同步調(diào)用與 Nagle's algorithm 會(huì)產(chǎn)生沖突,雖然 gRPC 在代碼中加入了 TCP_NODELAY 這個(gè) socketopt 但在 OS X 中是沒(méi)有效果的豫柬。后來(lái)通過(guò)設(shè)定 net.inet.tcp.delayed_ack = 0 來(lái)解決告希,同樣我們?cè)?linux 下也設(shè)置了 net.ipv4.tcp_low_latency = 1,這樣在 100M 帶寬下一次同步調(diào)用的時(shí)間在 500us 以下烧给。而且在實(shí)際應(yīng)用中燕偶,我們通過(guò) streaming 調(diào)用來(lái)解決大量重復(fù)數(shù)據(jù)傳輸?shù)膯?wèn)題,而不是通過(guò)反復(fù)的同步調(diào)用來(lái)傳相同的數(shù)據(jù)础嫡,這樣一次寫入可以在 5us 左右指么。其實(shí)批量寫入一直都是一個(gè)很好的解決性能問(wèn)題的方法S


心跳相關(guān)處理

  1. 心跳其實(shí)有兩個(gè)作用

    • 心跳保證客戶端和服務(wù)端的連接保活功能,服務(wù)端以此來(lái)判斷客戶端是否還在線
    • 心跳還需要維持移動(dòng)網(wǎng)絡(luò)的GGSN.
  2. 最常見的就是每隔固定時(shí)間(如4分半)發(fā)送心跳,但是這樣不夠智能.

    • 心跳時(shí)間太短,消耗流量/電量,增加服務(wù)器壓力.
    • 心跳時(shí)間太長(zhǎng),可能會(huì)被因?yàn)檫\(yùn)營(yíng)商的策略淘汰NAT表中的對(duì)應(yīng)項(xiàng)而被動(dòng)斷開連接
  3. 心跳算法 (參考Android微信智能心跳策略)

    • 維護(hù)移動(dòng)網(wǎng)GGSN(網(wǎng)關(guān)GPRS支持節(jié)點(diǎn))
      • 大部分移動(dòng)無(wú)線網(wǎng)絡(luò)運(yùn)營(yíng)商都在鏈路一段時(shí)間沒(méi)有數(shù)據(jù)通訊時(shí)榴鼎,會(huì)淘汰 NAT 表中的對(duì)應(yīng)項(xiàng)伯诬,造成鏈路中斷。NAT超時(shí)是影響TCP連接壽命的一個(gè)重要因素(尤其是國(guó)內(nèi))巫财,所以客戶端自動(dòng)測(cè)算NAT超時(shí)時(shí)間盗似,來(lái)動(dòng)態(tài)調(diào)整心跳間隔,是一個(gè)很重要的優(yōu)化點(diǎn)平项。
    • 參考微信的一套自適應(yīng)心跳算法:
      • 為了保證收消息及時(shí)性的體驗(yàn)赫舒,當(dāng)app處于前臺(tái)活躍狀態(tài)時(shí)悍及,使用固定心跳。
      • app進(jìn)入后臺(tái)(或者前臺(tái)關(guān)屏)時(shí)接癌,先用幾次最小心跳維持長(zhǎng)鏈接并鸵。然后進(jìn)入后臺(tái)自適應(yīng)心跳計(jì)算。這樣做的目的是盡量選擇用戶不活躍的時(shí)間段扔涧,來(lái)減少心跳計(jì)算可能產(chǎn)生的消息不及時(shí)收取影響。
  4. 精簡(jiǎn)心跳包届谈,保證一個(gè)心跳包大小在10字節(jié)之內(nèi), 根據(jù)APP前后臺(tái)狀態(tài)調(diào)整心跳包間隔 (主要是安卓)


弱網(wǎng)環(huán)境下的相關(guān)處理

  1. 網(wǎng)絡(luò)加速 cdn

    • 包括信令加速點(diǎn)和圖片CDN網(wǎng)絡(luò)
  2. 協(xié)議精簡(jiǎn)和壓縮

    • 使用壓縮算法,對(duì)數(shù)據(jù)包進(jìn)行壓縮
  3. TCP第一次通過(guò)域名連接上后枯夜,緩存IP,下次進(jìn)行IP直連艰山;若下次IP連接失敗湖雹,則重新走域名連接

  4. 對(duì)于大文件和圖片等, 使用斷點(diǎn)上傳和分段上傳

  5. 平衡網(wǎng)絡(luò)延遲和帶寬的影響

    • 在包大小小于1500字節(jié)時(shí), 盡量合并請(qǐng)求包. 減少請(qǐng)求
  6. ip就近接入

    • ip 直連(域名轉(zhuǎn)ip)
    • 域名解析(ip庫(kù)), 域名解析的耗時(shí)在移動(dòng)網(wǎng)絡(luò)中尤其慢
    • 計(jì)算距離用戶地理位置最近的同一運(yùn)營(yíng)商的接入點(diǎn)

斷線重連策略

掉線后,根據(jù)不同的狀態(tài)需要選擇不同的重連間隔曙搬。如果是本地網(wǎng)絡(luò)出錯(cuò)摔吏,并不需要定時(shí)去重連纵装,這時(shí)只需要監(jiān)聽網(wǎng)絡(luò)狀態(tài)征讲,等到網(wǎng)絡(luò)恢復(fù)后重連即可。如果網(wǎng)絡(luò)變化非常頻繁橡娄,特別是 App 處在后臺(tái)運(yùn)行時(shí)诗箍,對(duì)于重連也可以加上一定的頻率控制,在保證一定消息實(shí)時(shí)性的同時(shí)挽唉,避免造成過(guò)多的電量消耗滤祖。

  1. 斷線重連的最短間隔時(shí)間按單位秒(s)以4、8瓶籽、16...(最大不超過(guò)30)數(shù)列執(zhí)行匠童,以避免頻繁的斷線重連,從而減輕服務(wù)器負(fù)擔(dān)塑顺。當(dāng)服務(wù)端收到正確的包時(shí)汤求,此策略重置

  2. 有網(wǎng)絡(luò)但連接失敗的情況下,按單位秒(s)以間隔時(shí)間為2严拒、2首昔、4、4糙俗、8勒奇、8、16巧骚、16...(最大不超過(guò)120)的數(shù)列不斷重試

  3. 重連成功后的策略機(jī)制

    • 合并部分請(qǐng)求,以減少一次不必要的網(wǎng)絡(luò)請(qǐng)求來(lái)回的時(shí)間
    • 簡(jiǎn)化登錄后的同步請(qǐng)求赊颠,部分同步請(qǐng)求可以推遲到UI操作時(shí)進(jìn)行格二,如群成員信息刷新。
  4. 在重連Timer中竣蹦,為了防止雪崩效應(yīng)的出現(xiàn)顶猜,我們?cè)跈z測(cè)到socket失效(服務(wù)器異常),并不是立馬進(jìn)行重連痘括,而是讓客戶端隨機(jī)Sleep一段時(shí)間(或者上述其他策略)再去連接服務(wù)端长窄,這樣就可以使不同的客戶端在服務(wù)端重啟的時(shí)候不會(huì)同時(shí)去連接,從而造成雪崩效應(yīng)纲菌。


網(wǎng)絡(luò)切換怎么處理? 是否需要重連,是否重新登錄?

  1. 一般的話,有網(wǎng)絡(luò)切換(3g->4g->wifi->4g)就重連,重新走一遍整體流程

  2. 最好APP能以盡量少的通訊量來(lái)重新注冊(cè)服務(wù)器, 比如不再?gòu)姆?wù)器獲取配置信息,從上一次拉取的服務(wù)器配置的緩存數(shù)據(jù)直接讀取(如果服務(wù)器改變,最好能夠發(fā)一條通知給app更新)

  3. 如從wifi 切換到4G挠日、處于地鐵、WIFI邊緣地帶等翰舌,為避免造成重連風(fēng)暴(因?yàn)榫W(wǎng)絡(luò)不穩(wěn)定,會(huì)頻繁發(fā)起重連請(qǐng)求), 可以采用稍加延遲重連策略


服務(wù)端程序怎么擴(kuò)容/縮容? 水平擴(kuò)展方案?

  1. 采用業(yè)界常用的分布式服務(wù)發(fā)現(xiàn),配置方案. 如通過(guò)etcd來(lái)進(jìn)行服務(wù)發(fā)現(xiàn)和注冊(cè).

  2. 設(shè)計(jì)的各個(gè)模塊要能獨(dú)立化部署,設(shè)計(jì)為無(wú)狀態(tài)嚣潜,例如所謂的微服務(wù), 這樣才能夠很好的做服務(wù)的升級(jí)、擴(kuò)容, 保證無(wú)單點(diǎn)故障, 也方便灰度發(fā)布更新

  3. 動(dòng)態(tài)配置


群消息相關(guān)

  1. 消息是寫擴(kuò)散,還是讀擴(kuò)散: 群里面每個(gè)人都寫一次相同的消息,還是群里面都從同一個(gè)地方讀取這條相同消息?

    • 寫擴(kuò)散: 簡(jiǎn)單,但是群里面每個(gè)人都要寫一遍緩存.數(shù)據(jù)量有點(diǎn)大,而且增加網(wǎng)絡(luò)消耗(比如寫redis的時(shí)候).

    • 讀擴(kuò)算: 只寫一份到緩存,拉取的時(shí)候,從這個(gè)群緩存消息里面拉,需要增加一點(diǎn)邏輯處理,方便在所有群成員都拉取完后刪掉緩存數(shù)據(jù)(或者過(guò)期)

  2. 發(fā)送方式

    • 遍歷群成員,如果在線就依次發(fā)送, 但是群成員多,群活躍的時(shí)候,可能會(huì)增大壓力.

    • 遍歷群成員, 在線人員, 服務(wù)內(nèi)部流轉(zhuǎn)(rpc)的時(shí)候是否可以批量發(fā)送?

  3. 群方式

    • 在線的,msg只有一份到db中, index還是寫擴(kuò)散到cache和db中.
    • 離線的,緩存中,寫擴(kuò)散(msg和index),如果緩存失效,則穿透到db中拉取.
  4. 對(duì)于群消息,每條消息都需要拉取群成員的在線狀態(tài).如果存放在redis,拉取會(huì)太過(guò)頻繁.連接數(shù)會(huì)暴增,并發(fā)過(guò)高. 這樣可以增加一級(jí)本地緩存,把連接信息放到本地緩存(通過(guò)消耗內(nèi)存來(lái)減少網(wǎng)絡(luò)連接和請(qǐng)求)


客戶端減小電量消耗策略

  1. 不能影響手機(jī)休眠,采用alarm manager觸發(fā)心跳包

  2. 盡量減少網(wǎng)絡(luò)請(qǐng)求,最好能夠合并(或者一次發(fā)送多個(gè)請(qǐng)求). 批量椅贱、合并數(shù)據(jù)請(qǐng)求/發(fā)送

  3. 移動(dòng)網(wǎng)絡(luò)下載速度大于上傳速度,2G一次發(fā)送數(shù)據(jù)包不要太大,3G/4G一次發(fā)送多更省電.


消息是如何保證可達(dá)(不丟)/唯一/保序?

  1. 消息頭包含字段dup, 如果是重復(fù)遞送的消息,置位此字段,用來(lái)判定重復(fù)遞送

  2. 服務(wù)端緩存對(duì)應(yīng)的msgid列表, 客戶端下發(fā)已收到的最大msgid, 服務(wù)端根據(jù)客戶端收到的最大msgid來(lái)判斷小于此id的消息已經(jīng)全部被接收.這樣保證消息不丟.

  3. 服務(wù)端確保msgid生成器的極度高的可用性,并且遞增, 通過(guò)msgid的大小,來(lái)保證消息的順序

詳細(xì)說(shuō)明消息防丟失機(jī)制

為了達(dá)到任意一條消息都不丟的狀態(tài)懂算,最簡(jiǎn)單的方案是手機(jī)端對(duì)收到的每條消息都給服務(wù)器進(jìn)行一次ack確認(rèn),但該方案在手機(jī)端和服務(wù)器之間的交互過(guò)多庇麦,并且也會(huì)遇到在弱網(wǎng)絡(luò)情況下ack丟失等問(wèn)題计技。因此,引入sequence機(jī)制

  1. 每個(gè)用戶都有42億的sequnence空間(從1到UINT_MAX),從小到大連續(xù)分配
  2. 每個(gè)用戶的每條消息都需要分配一個(gè)sequence
  3. 服務(wù)器存儲(chǔ)有每個(gè)用戶已經(jīng)分配到的最大sequence
  4. 手機(jī)端存儲(chǔ)有已收取消息的最大sequence


    image.png

** 方案優(yōu)點(diǎn) **

  1. 根據(jù)服務(wù)器和手機(jī)端之間sequence的差異,可以很輕松的實(shí)現(xiàn)增量下發(fā)手機(jī)端未收取下去的消息

  2. 對(duì)于在弱網(wǎng)絡(luò)環(huán)境差的情況山橄,丟包情況發(fā)生概率是比較高的酸役,此時(shí)經(jīng)常會(huì)出現(xiàn)服務(wù)器的回包不能到達(dá)手機(jī)端的現(xiàn)象。由于手機(jī)端只會(huì)在確切的收取到消息后才會(huì)更新本地的sequence驾胆,所以即使服務(wù)器的回包丟了涣澡,手機(jī)端等待超時(shí)后重新拿舊的sequence上服務(wù)器收取消息,同樣是可以正確的收取未下發(fā)的消息丧诺。

  3. 由于手機(jī)端存儲(chǔ)的sequence是確認(rèn)收到消息的最大sequence入桂,所以對(duì)于手機(jī)端每次到服務(wù)器來(lái)收取消息也可以認(rèn)為是對(duì)上一次收取消息的確認(rèn)。一個(gè)帳號(hào)在多個(gè)手機(jī)端輪流登錄的情況下驳阎,只要服務(wù)器存儲(chǔ)手機(jī)端已確認(rèn)的sequence抗愁,那就可以簡(jiǎn)單的實(shí)現(xiàn)已確認(rèn)下發(fā)的消息不會(huì)重復(fù)下發(fā),不同手機(jī)端之間輪流登錄不會(huì)收到其他手機(jī)端已經(jīng)收取到的消息呵晚。


通信方式(TCP/UDP/HTTP)同時(shí)使用tcp和http.

  1. IM系統(tǒng)的主要需求:包括賬號(hào)蜘腌、關(guān)系鏈、在線狀態(tài)顯示饵隙、消息交互(文本撮珠、圖片、語(yǔ)音)金矛、實(shí)時(shí)音視頻

  2. http模式(short鏈接)和 tcp 模式(long 鏈接)芯急,分別應(yīng)對(duì)狀態(tài)協(xié)議和數(shù)據(jù)傳輸協(xié)議

  3. 保持長(zhǎng)連接的時(shí)候,用TCP. 因?yàn)樾枰S時(shí)接受信息. 要維持長(zhǎng)連接就只能選TCP,而非UDP

  4. 獲取其他非及時(shí)性的資源的時(shí)候,采用http短連接. 為啥不全部用TCP協(xié)議呢? 用http協(xié)議有什么好處?

    • 目前大部分功能可以通過(guò)TCP來(lái)實(shí)現(xiàn).
    • 文件上傳下載的話,就非http莫屬了
      • 支持?jǐn)帱c(diǎn)續(xù)傳和分片上傳.
    • 離線消息用拉模式勺届,避免 tcp 通道壓力過(guò)大,影響即時(shí)消息下發(fā)效率
    • 大涂鴉娶耍、文件采用存儲(chǔ)服務(wù)上傳免姿,避免 tcp 通道壓力過(guò)大
  5. IM到底該用UDP還是TCP協(xié)議

    • UDP和TCP各有各的應(yīng)用場(chǎng)景,作為IM來(lái)說(shuō)榕酒,早期的IM因?yàn)榉?wù)端資源(服務(wù)器硬件胚膊、網(wǎng)絡(luò)帶寬等)比較昂貴且沒(méi)有更好的辦法來(lái)分擔(dān)性能負(fù)載,所以很多時(shí)候會(huì)考慮使用UDP想鹰,這其中主要是早期的QQ為代表紊婉。

    • TCP的服務(wù)端負(fù)載已經(jīng)有了很好的解決方案,加之服務(wù)器資源成本的下降杖挣,目前很多IM、消息推送解決方案也都在使用TCP作為傳輸層協(xié)議刚陡。不過(guò)惩妇,UDP也并未排除在IM、消息推送的解決方案之外筐乳,比如:弱網(wǎng)絡(luò)通信(包括跨國(guó)的高延遲網(wǎng)絡(luò)環(huán)境)歌殃、物聯(lián)網(wǎng)通信、IM中的實(shí)時(shí)音視頻通信等等場(chǎng)景下蝙云,UDP依然是首選項(xiàng)氓皱。

    • 關(guān)于IM到底該選擇UDP還是TCP,這是個(gè)仁者見仁智者見智的問(wèn)題勃刨,沒(méi)有必要過(guò)于糾結(jié)波材,請(qǐng)從您的IM整體應(yīng)用場(chǎng)景、開發(fā)代價(jià)身隐、部署和運(yùn)營(yíng)成本等方面綜合考慮廷区,相信能找到你要的答案。


服務(wù)器和客戶端的通信協(xié)議選擇

  1. 常用IM協(xié)議:IM協(xié)議選擇原則一般是:易于拓展贾铝,方便覆蓋各種業(yè)務(wù)邏輯隙轻,同時(shí)又比較節(jié)約流量。后一點(diǎn)的需求在移動(dòng)端IM上尤其重要?

    • xmpp: 協(xié)議開源垢揩,可拓展性強(qiáng)玖绿,在各個(gè)端(包括服務(wù)器)有各種語(yǔ)言的實(shí)現(xiàn),開發(fā)者接入方便。但是缺點(diǎn)也是不少:XML表現(xiàn)力弱,有太多冗余信息自娩,流量大铐料,實(shí)際使用時(shí)有大量天坑阳似。

    • MQTT: 協(xié)議簡(jiǎn)單熊响,流量少昌抠,但是它并不是一個(gè)專門為IM設(shè)計(jì)的協(xié)議锄贷,多使用于推送. 需要自己在業(yè)務(wù)上實(shí)現(xiàn)群,好友相關(guān)等等. 適合推送業(yè)務(wù)苍姜,適合直播IM場(chǎng)景牢酵。

    • SIP: 多用于VOIP相關(guān)的模塊,是一種文本協(xié)議. sip信令控制比較復(fù)雜

    • 私有協(xié)議: 自己實(shí)現(xiàn)協(xié)議.大部分主流IM APP都是是使用私有協(xié)議衙猪,一個(gè)被良好設(shè)計(jì)的私有協(xié)議一般有如下優(yōu)點(diǎn):高效馍乙,節(jié)約流量(一般使用二進(jìn)制協(xié)議),安全性高垫释,難以破解丝格。

  2. 協(xié)議設(shè)計(jì)的考量:

    • 網(wǎng)絡(luò)數(shù)據(jù)大小——占用帶寬,傳輸效率:雖然對(duì)單個(gè)用戶來(lái)說(shuō)棵譬,數(shù)據(jù)量傳輸很小显蝌,但是對(duì)于服務(wù)器端要承受眾多的高并發(fā)數(shù)據(jù)傳輸,必須要考慮到數(shù)據(jù)占用帶寬订咸,盡量不要有冗余數(shù)據(jù)曼尊,這樣才能夠少占用帶寬,少占用資源脏嚷,少網(wǎng)絡(luò)IO骆撇,提高傳輸效率;

    • 網(wǎng)絡(luò)數(shù)據(jù)安全性——敏感數(shù)據(jù)的網(wǎng)絡(luò)安全:對(duì)于相關(guān)業(yè)務(wù)的部分?jǐn)?shù)據(jù)傳輸都是敏感數(shù)據(jù)父叙,所以必須考慮對(duì)部分傳輸數(shù)據(jù)進(jìn)行加密

    • 編碼復(fù)雜度——序列化和反序列化復(fù)雜度神郊,效率,數(shù)據(jù)結(jié)構(gòu)的可擴(kuò)展性

    • 協(xié)議通用性——大眾規(guī)范:數(shù)據(jù)類型必須是跨平臺(tái)趾唱,數(shù)據(jù)格式是通用的

  1. 常用序列化協(xié)議比較
    • 提供序列化和反序列化庫(kù)的開源協(xié)議: pb涌乳,Thrift. 擴(kuò)展相當(dāng)方便,序列化和反序列化方便

    • 文本化協(xié)議: xml甜癞,json. 序列化爷怀,反序列化容易,但是占用體積大.

  1. 定義協(xié)議考量
    • 包數(shù)據(jù)可以考慮壓縮,減小數(shù)據(jù)包大小

    • 包數(shù)據(jù)考慮加密,保證數(shù)據(jù)安全

    • 協(xié)議里面有些字段uint64,可以適當(dāng)調(diào)整為uint32.減小包頭大小

    • 協(xié)議頭里面最好包含seq_num

      • 這個(gè)是為了異步化的支持。這種消息通道最重要的是解決通道問(wèn)題带欢,所有消息處理不能是同步的运授,必須是異步的,你發(fā)一個(gè)消息出去乔煞,ABC三個(gè)包吁朦,你收到XYZ三個(gè)包之后,你怎么知道它是對(duì)應(yīng)的渡贾,就是對(duì)應(yīng)關(guān)系的話我們?cè)趺刺幚矶阂耍褪羌右粋€(gè)ID

IM系統(tǒng)架構(gòu)設(shè)計(jì)的重點(diǎn)考量點(diǎn)

  1. 編碼角度:采用高效的網(wǎng)絡(luò)模型,線程模型,I/O處理模型纺讲,合理的數(shù)據(jù)庫(kù)設(shè)計(jì)和操作語(yǔ)句的優(yōu)化擂仍;

  2. 垂直擴(kuò)展:通過(guò)提高單服務(wù)器的硬件資源或者網(wǎng)絡(luò)資源來(lái)提高性能;

  3. 水平擴(kuò)展:通過(guò)合理的架構(gòu)設(shè)計(jì)和運(yùn)維方面的負(fù)載均衡策略將負(fù)載分擔(dān)熬甚,有效提高性能逢渔;后期甚至可以考慮加入數(shù)據(jù)緩存層,突破IO瓶頸乡括;

  4. 系統(tǒng)的高可用性:防止單點(diǎn)故障肃廓;

  5. 在架構(gòu)設(shè)計(jì)時(shí)做到業(yè)務(wù)處理和數(shù)據(jù)的分離,從而依賴分布式的部署使得在單點(diǎn)故障時(shí)能保證系統(tǒng)可用诲泌。

  6. 對(duì)于關(guān)鍵獨(dú)立節(jié)點(diǎn)可以采用雙機(jī)熱備技術(shù)進(jìn)行切

  7. 數(shù)據(jù)庫(kù)數(shù)據(jù)的安全性可以通過(guò)磁盤陣列的冗余配置和主備數(shù)據(jù)庫(kù)來(lái)解決盲赊。


TCP 擁堵解決方案

TCP的擁塞控制由4個(gè)核心算法組成:“慢啟動(dòng)”(Slow Start)、“擁塞避免”(Congestion voidance)敷扫、“快速重傳 ”(Fast Retransmit)哀蘑、“快速恢復(fù)”(Fast Recovery)。


怎么判斷kafka隊(duì)列是否滯后了?

kafka隊(duì)列葵第,沒(méi)有滿的概念, 只有消費(fèi)滯后/堆積的概念

  1. 通過(guò)offset monitor 監(jiān)控對(duì)kafka進(jìn)行實(shí)時(shí)監(jiān)控

  2. 對(duì)于kafka

    • 本身就是一個(gè)分布式绘迁,本身就能給支持這種線性的擴(kuò)展,所以不會(huì)面臨這種問(wèn)題羹幸。

    • 你會(huì)寫數(shù)據(jù)不消費(fèi)么脊髓。


操作緩存和數(shù)據(jù)庫(kù)的方案

  1. 寫: 先寫數(shù)據(jù)庫(kù),成功后,更新緩存

  2. 讀: 先讀緩存, 沒(méi)有數(shù)據(jù)則穿透到db.

但是, 假如我寫數(shù)據(jù)庫(kù)成功,更新緩存失敗了. 那下次讀的時(shí)候,就會(huì)讀到臟數(shù)據(jù)(數(shù)據(jù)不一致),這種情況怎么處理?

方案:

  1. 先淘汰緩存,再寫數(shù)據(jù)庫(kù). 但是如果在并發(fā)的時(shí)候,也可能出現(xiàn)不一致的問(wèn)題,就是假如淘汰掉緩存后,還沒(méi)有及時(shí)寫入db, 這個(gè)時(shí)候來(lái)了讀請(qǐng)求,就會(huì)直接從db里面讀取舊數(shù)據(jù).

    • 因此,需要嚴(yán)格保證針對(duì)同一個(gè)數(shù)據(jù)的操作都是串行的.
  2. 由于數(shù)據(jù)庫(kù)層面的讀寫并發(fā)辫愉,引發(fā)的數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)不一致的問(wèn)題(本質(zhì)是后發(fā)生的讀請(qǐng)求先返回了)栅受,可能通過(guò)兩個(gè)小的改動(dòng)解決:

    • 修改服務(wù)Service連接池,id取模選取服務(wù)連接恭朗,能夠保證同一個(gè)數(shù)據(jù)的讀寫都落在同一個(gè)后端服務(wù)上

    • 修改數(shù)據(jù)庫(kù)DB連接池屏镊,id取模選取DB連接,能夠保證同一個(gè)數(shù)據(jù)的讀寫在數(shù)據(jù)庫(kù)層面是串行的


數(shù)據(jù)庫(kù)分庫(kù)分表

數(shù)據(jù)庫(kù)為什么要分庫(kù)分表? 什么情況下分庫(kù)分表 ?

  1. 解決磁盤系統(tǒng)最大文件限制

  2. 減少增量數(shù)據(jù)寫入時(shí)的鎖 對(duì)查詢的影響痰腮,減少長(zhǎng)時(shí)間查詢?cè)斐傻谋礞i而芥,影響寫入操作等鎖競(jìng)爭(zhēng)的情況. (表鎖和行鎖) . 避免單張表間產(chǎn)生的鎖競(jìng)爭(zhēng),節(jié)省排隊(duì)的時(shí)間開支膀值,增加呑吐量

  3. 由于單表數(shù)量下降棍丐,常見的查詢操作由于減少了需要掃描的記錄,使得單表單次查詢所需的檢索行數(shù)變少沧踏,減少了磁盤IO歌逢,時(shí)延變短

  1. 一臺(tái)服務(wù)器的資源(CPU、磁盤翘狱、內(nèi)存秘案、IO等)是有限的,最終數(shù)據(jù)庫(kù)所能承載的數(shù)據(jù)量、數(shù)據(jù)處理能力都將遭遇瓶頸阱高。分庫(kù)的目的是降低單臺(tái)服務(wù)器負(fù)載赚导,切分原則是根據(jù)業(yè)務(wù)緊密程度拆分,缺點(diǎn)是跨數(shù)據(jù)庫(kù)無(wú)法聯(lián)表查詢
  1. 當(dāng)數(shù)據(jù)量超大的時(shí)候赤惊,B-Tree索引的作用就沒(méi)那么明顯了吼旧。如果數(shù)據(jù)量巨大,將產(chǎn)生大量隨機(jī)I/O荐捻,同時(shí)數(shù)據(jù)庫(kù)的響應(yīng)時(shí)間將大到不可接受的程度黍少。
    • 數(shù)據(jù)量超大的時(shí)候,B-TREE的樹深度會(huì)變深处面,從根節(jié)點(diǎn)到葉子節(jié)點(diǎn)要經(jīng)過(guò)的IO次數(shù)也會(huì)增大厂置。當(dāng)IO層數(shù)超過(guò)4層之后,就會(huì)變得很慢魂角,其實(shí)4層IO昵济,存儲(chǔ)的數(shù)據(jù)都是TB級(jí)別的了,除非你的數(shù)據(jù)類型都是INT等小類型的野揪。也不能說(shuō)BTREE不起作用访忿,只是說(shuō)作用沒(méi)那么明顯了。
    • 數(shù)據(jù)量巨大斯稳,就一定是隨機(jī)IO嗎海铆?這不一定的,如果都是主鍵查詢挣惰,10E條記錄都可以很快返回結(jié)果卧斟。當(dāng)用二級(jí)索引來(lái)查詢的時(shí)候,就變成隨機(jī)IO了憎茂,響應(yīng)時(shí)間是會(huì)變慢珍语,這也要看數(shù)據(jù)的分布。另外他也沒(méi)說(shuō)存儲(chǔ)介質(zhì)竖幔,如果用SSD盤板乙,隨機(jī)IO比SAS的強(qiáng)100倍,性能也是不錯(cuò)的

Golang的goroutine

  1. goroutine都是用戶態(tài)的調(diào)度, 協(xié)程切換只是簡(jiǎn)單地改變執(zhí)行函數(shù)棧拳氢,不涉及內(nèi)核態(tài)與用戶態(tài)轉(zhuǎn)化, 上下文切換的開銷比較小.

  2. 創(chuàng)建一個(gè)goroutine需要大概2k(V1.4)左右的椖汲眩空間.

  3. go有搶占式調(diào)度:如果一個(gè)Goroutine一直占用CPU,長(zhǎng)時(shí)間沒(méi)有被調(diào)度過(guò)馋评,就會(huì)被runtime搶占掉

是不是表示,在內(nèi)存夠用的條件下, 創(chuàng)建一定量(比如,30w,50w)的goroutine, 不會(huì)因?yàn)閏pu調(diào)度原因?qū)е滦阅芟陆堤?

  1. 如果系統(tǒng)里面goroutine太多, 可能原因之一就是因?yàn)槊總€(gè)goroutine處理時(shí)間過(guò)長(zhǎng),那么就需要查看為啥處理耗時(shí)較長(zhǎng).
  1. 給出大概數(shù)據(jù)放接,24核,64G的服務(wù)器上栗恩,在QoS為message at least透乾,純粹推洪燥,消息體256B1kB情況下,單個(gè)實(shí)例100w實(shí)際用戶(200w+)協(xié)程乳乌,峰值可以達(dá)到25w的QPS...內(nèi)存可以穩(wěn)定在25G左右捧韵,gc時(shí)間在200~800ms左右(還有優(yōu)化空間)。 (來(lái)自360消息系統(tǒng)分享)

長(zhǎng)連接接入層的net連接管理

長(zhǎng)連接接入層的net連接很多汉操,一般單臺(tái)服務(wù)器可以有幾十萬(wàn)再来、甚至上百萬(wàn),那么怎么管理這些連接 ? 后端數(shù)據(jù)來(lái)了, 怎么快速找到這個(gè)請(qǐng)求對(duì)應(yīng)的連接呢 ? 連接和用戶如何對(duì)應(yīng)

管理tcp長(zhǎng)連接

  1. 一個(gè)連接結(jié)構(gòu). 包含tcp連接信息,上次通信時(shí)間, 加解密sharekey, clientaddr. 還包含一個(gè)用戶結(jié)構(gòu)
    • 用戶結(jié)構(gòu)里面包含uid, deviceid. name ,version ...., 還包含上面的這個(gè)連接, 兩者一一對(duì)應(yīng).

    • 不用map來(lái)管理, 而是把tcp連接信息和user信息來(lái)進(jìn)行一一對(duì)應(yīng),如果map的話,幾百萬(wàn)可能查找起來(lái)比較慢.

    • 登錄請(qǐng)求的時(shí)候,可以根據(jù)這個(gè)tcp連接信息,獲取user信息,但是此時(shí)user信息基本沒(méi)有填充什么數(shù)據(jù),所以就需要根據(jù)登錄來(lái)填充user信息結(jié)構(gòu). 關(guān)鍵是: 在當(dāng)前Access接入服務(wù)里面,會(huì)有一個(gè)useMap,會(huì)把uid和user信息對(duì)應(yīng)起來(lái),可以用來(lái)判斷此uid,是否在本實(shí)例上登錄過(guò)

    • 返回?cái)?shù)據(jù)的時(shí)候, 可以根據(jù)這個(gè)uid,來(lái)獲取對(duì)應(yīng)的user結(jié)構(gòu),然后通過(guò)這個(gè)結(jié)構(gòu)可以獲取對(duì)應(yīng)的tcp 連接信息, 可以進(jìn)行發(fā)送信息.

  1. 另外,登錄登出的時(shí)候,會(huì)有另外的連接信息(uid/topic/protoType/addr...) 添加刪除到用戶中心

    • 登錄成功:UseAddConn
    • 登出下線:UserDelConn
    • 這里的連接信息,供其他遠(yuǎn)程服務(wù)調(diào)用,如Oracle.
  2. 如果有多個(gè)Access接入層, 每個(gè)接入層都會(huì)有一個(gè)useMap結(jié)構(gòu).

    • 如果多個(gè)終端登錄同一個(gè)賬號(hào),而且在不同的Access,那么就不能通過(guò)useMap來(lái)踢出,就需要上步說(shuō)的用戶中心來(lái)管理踢出
    • 多個(gè)Access,意味著多個(gè)useMap,那么就需要保證,從某個(gè)Access下發(fā)的請(qǐng)求,一定會(huì)回到當(dāng)前Access. 怎么保證呢? 把當(dāng)前Access的ip:addr一直下發(fā)下去,然后返回的時(shí)候,根據(jù)下發(fā)的Access的ip:addr來(lái)回到對(duì)應(yīng)的Access.
    • 然后根據(jù)uid,來(lái)獲取當(dāng)前uid對(duì)應(yīng)的user結(jié)構(gòu)和tcp連接結(jié)構(gòu).

數(shù)據(jù)結(jié)構(gòu): map/hash(紅黑樹)

管理收發(fā)異常,請(qǐng)求回應(yīng)ack, 超時(shí)

  1. 利用map數(shù)據(jù)結(jié)構(gòu), 發(fā)送(publish)完消息后,立即通過(guò)msgid和uid,把對(duì)應(yīng)的消息體添加到map結(jié)構(gòu).

  2. 收到回應(yīng)后,刪除對(duì)應(yīng)的map結(jié)構(gòu).

  3. 超時(shí)后,重新提交OfflineDeliver. 然后刪除對(duì)應(yīng)的map結(jié)構(gòu).

異步,并發(fā)的時(shí)候,rpc 框架,怎么知道哪個(gè)請(qǐng)求是哪個(gè)的呢 ?

  1. client線程每次通過(guò)socket調(diào)用一次遠(yuǎn)程接口前磷瘤,生成一個(gè)唯一的ID芒篷,即requestID(requestID必需保證在一個(gè)Socket連接里面是唯一的),一般常常使用AtomicLong從0開始累計(jì)數(shù)字生成唯一ID,或者利用時(shí)間戳來(lái)生成唯一ID.
  1. grpc 也需要服務(wù)發(fā)現(xiàn). grpc服務(wù)可能有一個(gè)實(shí)例. 2個(gè), 甚至多個(gè)? 可能某個(gè)服務(wù)會(huì)掛掉/宕機(jī). 可以利用zookeeper來(lái)管理.
  1. 同步 RPC 調(diào)用一直會(huì)阻塞直到從服務(wù)端獲得一個(gè)應(yīng)答采缚,這與 RPC 希望的抽象最為接近针炉。另一方面網(wǎng)絡(luò)內(nèi)部是異步的,并且在許多場(chǎng)景下能夠在不阻塞當(dāng)前線程的情況下啟動(dòng) RPC 是非常有用的扳抽。
    在多數(shù)語(yǔ)言里篡帕,gRPC 編程接口同時(shí)支持同步和異步的特點(diǎn)。

  2. gRPC 允許客戶端在調(diào)用一個(gè)遠(yuǎn)程方法前指定一個(gè)最后期限值贸呢。這個(gè)值指定了在客戶端可以等待服務(wù)端多長(zhǎng)時(shí)間來(lái)應(yīng)答镰烧,超過(guò)這個(gè)時(shí)間值 RPC 將結(jié)束并返回DEADLINE_EXCEEDED錯(cuò)誤。在服務(wù)端可以查詢這個(gè)期限值來(lái)看是否一個(gè)特定的方法已經(jīng)過(guò)期楞陷,或者還剩多長(zhǎng)時(shí)間來(lái)完成這個(gè)方法怔鳖。 各語(yǔ)言來(lái)指定一個(gè)截止時(shí)間的方式是不同的

服務(wù)性能方面的考慮點(diǎn)

  1. 編碼角度:

    • 采用高效的網(wǎng)絡(luò)模型,線程模型固蛾,I/O處理模型结执,合理的數(shù)據(jù)庫(kù)設(shè)計(jì)和操作語(yǔ)句的優(yōu)化;
  2. 垂直擴(kuò)展:

    • 通過(guò)提高單服務(wù)器的硬件資源或者網(wǎng)絡(luò)資源來(lái)提高性能魏铅;
  3. 水平擴(kuò)展:

    • 通過(guò)合理的架構(gòu)設(shè)計(jì)和運(yùn)維方面的負(fù)載均衡策略將負(fù)載分擔(dān)昌犹,有效提高性能坚芜;后期甚至可以考慮加入數(shù)據(jù)緩存層览芳,突破IO瓶頸;
  4. 系統(tǒng)的高可用性:

    • 防止單點(diǎn)故障鸿竖;
  5. 在架構(gòu)設(shè)計(jì)時(shí)做到業(yè)務(wù)處理和數(shù)據(jù)的分離沧竟,從而依賴分布式的部署使得在單點(diǎn)故障時(shí)能保證系統(tǒng)可用。

  6. 對(duì)于關(guān)鍵獨(dú)立節(jié)點(diǎn)可以采用雙機(jī)熱備技術(shù)進(jìn)行切換缚忧。

  7. 數(shù)據(jù)庫(kù)數(shù)據(jù)的安全性可以通過(guò)磁盤陣列的冗余配置和主備數(shù)據(jù)庫(kù)來(lái)解決悟泵。


服務(wù)器的瓶頸分析

通過(guò)壓測(cè)得知gRPC是瓶頸影響因素之一,為啥是grpc? 為啥消耗cpu? 怎么解決? 網(wǎng)絡(luò)一定不會(huì)影響吞吐.

  1. 采用uarmy 方式. 可以考慮采用streaming方式. 批量發(fā)送,提高效率

    • uarmy方式一對(duì)一,并發(fā)增大的時(shí)候,連接數(shù)會(huì)增大

    • streaming方式的話,就是合并多個(gè)請(qǐng)求(批量打包請(qǐng)求/響應(yīng)), 減少網(wǎng)絡(luò)交互, 減少連接

    • 做過(guò)streaming 的壓測(cè)闪水,性能說(shuō)比 unary 高一倍還多

  2. 一般服務(wù)器都會(huì)有個(gè)拋物線規(guī)律, 隨著并發(fā)數(shù)的增大,會(huì)逐漸消耗并跑滿(cpu/內(nèi)存/網(wǎng)絡(luò)帶寬/磁盤io), 隨之帶來(lái)的就是響應(yīng)時(shí)間變慢(時(shí)延Latency變成長(zhǎng)),而qps/吞吐量也上不去.

  1. 對(duì)于grpc 而言, 并發(fā)數(shù)增多后,能看到實(shí)際效果就是延遲增大,有部分請(qǐng)求的一次請(qǐng)求響應(yīng)時(shí)間達(dá)到了5s左右(ACCESS/PUSH), 這樣說(shuō)明時(shí)延太長(zhǎng), qps/吞吐量 = 并發(fā)數(shù)/響應(yīng)時(shí)間. 響應(yīng)時(shí)間太長(zhǎng),吞吐當(dāng)然上不去.
    • 為啥響應(yīng)時(shí)間這么長(zhǎng)了? 是因?yàn)閏pu跑滿了么?

    • 還有一個(gè)原因倒是響應(yīng)慢,那就是最終請(qǐng)求會(huì)到Oracle服務(wù), 而oracle會(huì)請(qǐng)求數(shù)據(jù)資源(cache/db), oracle的設(shè)計(jì)中請(qǐng)求資源的并發(fā)增多(連接數(shù)也增多),導(dǎo)致請(qǐng)求資源的時(shí)延增長(zhǎng),因此返回到上級(jí)grpc的調(diào)用也會(huì)增大時(shí)延.

    • 因此關(guān)鍵最終又回到了 cpu/內(nèi)存/網(wǎng)絡(luò)帶寬/磁盤io這里了

  1. rpc 而言, 連接數(shù)增多了,會(huì)導(dǎo)致:

    • 類似tcp長(zhǎng)連接一樣, 每個(gè)連接肯定要分配一定的內(nèi)存

    • 要同時(shí)處理這么多連接,每個(gè)連接都有相應(yīng)的事務(wù), cpu的處理能力要強(qiáng)

  2. 后來(lái)經(jīng)過(guò)調(diào)查我們發(fā)現(xiàn) gRPC 的同步調(diào)用與 Nagle's algorithm 會(huì)產(chǎn)生沖突糕非,雖然 gRPC 在代碼中加入了 TCP_NODELAY 這個(gè) socketopt 但在 OS X 中是沒(méi)有效果的。后來(lái)通過(guò)設(shè)定 net.inet.tcp.delayed_ack = 0 來(lái)解決,同樣我們?cè)?linux 下也設(shè)置了 net.ipv4.tcp_low_latency = 1朽肥,這樣在 100M 帶寬下一次同步調(diào)用的時(shí)間在 500us 以下禁筏。而且在實(shí)際應(yīng)用中,我們通過(guò) streaming 調(diào)用來(lái)解決大量重復(fù)數(shù)據(jù)傳輸?shù)膯?wèn)題衡招,而不是通過(guò)反復(fù)的同步調(diào)用來(lái)傳相同的數(shù)據(jù)篱昔,這樣一次寫入可以在 5us 左右。其實(shí)批量寫入一直都是一個(gè)很好的解決性能問(wèn)題的方法


如何快速接入服務(wù)端的接入層

如果服務(wù)器在北京, 客戶端在廣州, 如何能夠快速接入? 除了走cdn還有其他方式?jīng)] ?

  1. 如果只有一個(gè)數(shù)據(jù)中心, 暫時(shí)除了cdn加速, 沒(méi)有其他方法.

  2. 如果有兩個(gè)數(shù)據(jù)中心, 可以采取就近原則,但是需要兩個(gè)數(shù)據(jù)中心的數(shù)據(jù)進(jìn)行同步

  3. 就近接入:就是利用DNS服務(wù)找到離用戶最近的機(jī)器始腾,從而達(dá)到最短路徑提供服務(wù)

怎么提高在IM領(lǐng)域的能力 ?

  1. 要能在不壓測(cè)的情況下,就能夠預(yù)估出系統(tǒng)能夠支持的qps. 要能夠粗略估算出一次db的請(qǐng)求耗時(shí)多久, 一次redis的請(qǐng)求耗時(shí)多少, 一次rpc調(diào)用的請(qǐng)求耗時(shí)多少?
    • 系統(tǒng)中有哪些是比較耗時(shí),比較消耗cpu的.
  1. 所有系統(tǒng), 一定都是分為幾層, 從上層到底層, 每一步的請(qǐng)求是如何的? 在每個(gè)層耗時(shí)咋樣?

    • 系統(tǒng)有沒(méi)有引入其他資源

    • 性能瓶頸無(wú)法是cpu/io.

    • db查詢慢,是為啥慢? 慢一定有原因的?

      • 查詢一條sql語(yǔ)句的時(shí)間大致在0.2-0.5ms(在表數(shù)據(jù)量不大的情況下, 是否根據(jù)索引id來(lái)查詢,區(qū)別不大.)
    • 單臺(tái)機(jī), qps為8k, 是比較少的. qps: 8k, 那么平均請(qǐng)求響應(yīng)時(shí)間: 1/8ms=0.125ms, qps為8k, 那么5臺(tái)機(jī)器, qps就是4w, 同時(shí)10w人在線, 收發(fā)算一個(gè)qps的話,那么qps減半, 那就是2w qps, 10w同時(shí)在線, 每個(gè)人3-4s發(fā)一次消息, 需要qps到3w.

    • 之前測(cè)試redis的時(shí)候, 有測(cè)試過(guò),如果并發(fā)太高,會(huì)導(dǎo)致拉取redis耗時(shí)較長(zhǎng),超過(guò)3s左右.

    • 正常情況下,一個(gè)人發(fā)送一條消息需要耗時(shí)至少5s左右(6-8個(gè)字).

  2. 要深入提高IM技術(shù), 就必須要能夠?qū)W會(huì)分析性能, 找到性能瓶頸, 并解決掉.

    • 還要看別人如微信的一些做法
  1. 架構(gòu)都是逐步改造的, 每個(gè)階段有每個(gè)階段的架構(gòu), 一般架構(gòu),初始都是三層/四層架構(gòu).
    然后開始改造, 改造第一階段都是拆分服務(wù),按邏輯拆分,按業(yè)務(wù)拆分, 合并資源請(qǐng)求,減少并發(fā)數(shù),減少連接數(shù).

  2. 要經(jīng)常關(guān)注一些大數(shù)據(jù), 比如注冊(cè)用戶數(shù), 日活, 月活, 留存. 要對(duì)數(shù)據(jù)敏感, 為什么一直不變, 為什么突然增高, 峰值是多少? 目前能抗住多少 ?

  3. 關(guān)注系統(tǒng)性能指標(biāo),cpu,內(nèi)存,網(wǎng)絡(luò),磁盤等數(shù)據(jù), 經(jīng)常觀測(cè), 看看有沒(méi)有異常, 做到提前發(fā)現(xiàn)問(wèn)題,而不是等到問(wèn)題出現(xiàn)了再進(jìn)行解決, 就是是出現(xiàn)問(wèn)題了再進(jìn)行解決, 也要保證解決時(shí)間是分鐘級(jí)別的.

    • 完全理解系統(tǒng)底層工具的含義,如sar,iosta,dstat,vmstat,top等,這些數(shù)據(jù)要經(jīng)常觀察,經(jīng)持莨簦看

    • 保證整套系統(tǒng)中所涉及的各個(gè)部分都是白盒的

      • 依賴的其他服務(wù)是誰(shuí)負(fù)責(zé),部署情況,在哪個(gè)機(jī)房

      • 使用的資源情況,redis內(nèi)存多大 ? mysql 數(shù)據(jù)庫(kù)多少? 表多少? 主從怎么分布 ? 對(duì)于消息: 一主兩從,32庫(kù),32表. 對(duì)于好友數(shù)據(jù):一主一從,128表. 對(duì)于朋友圈,按月分表.

總結(jié)&如何思考問(wèn)題和提升自我能力

  1. 如果沒(méi)有自己思考,現(xiàn)有的東西都是合理的, 這顯然是不行的.
    • 每看一個(gè)東西,都要思考, 這個(gè)東西合不合理? 是否可以優(yōu)化? 有哪些類似的? 要想如果怎么樣

    • 例如:剛開始接觸xxx項(xiàng)目的時(shí)候,覺得這個(gè)架構(gòu)不錯(cuò),覺得不用優(yōu)化了,但是后面需要大規(guī)模推廣后,xxx就提出了一些優(yōu)化點(diǎn), 通過(guò)量級(jí)的提高,暴露了一些問(wèn)題

      • 并發(fā)大后,mysql慢請(qǐng)求問(wèn)題

      • 并發(fā)大后,請(qǐng)求資源并發(fā)太多,連接數(shù)太多問(wèn)題,因此需要合并資源請(qǐng)求

      • Access接入層長(zhǎng)連接的問(wèn)題, Access接入層服務(wù)升級(jí)不方便, 因此需要拆分Access長(zhǎng)連接,提升穩(wěn)定性.方便服務(wù)升級(jí).

  1. 除了熟悉代碼框架外, 一定還要深入到細(xì)節(jié), 比如golang的底層優(yōu)化, 系統(tǒng)級(jí)別的優(yōu)化.

  2. 圍繞im領(lǐng)域思考問(wèn)題和量級(jí), 當(dāng)前的量級(jí)是什么級(jí)別,然后需要考慮更高級(jí)別要做的事情.

    • 當(dāng)前級(jí)別為w級(jí)別的時(shí)候, 就要考慮十萬(wàn)級(jí)別該做的事,十萬(wàn)級(jí)別后,就要考慮百萬(wàn), 不一定要馬上做,但是一定要先想,先考慮,目前性能如何,怎么擴(kuò)展? 怎么重構(gòu)?
  3. 了解業(yè)界相關(guān)技術(shù)方案, 了解別人踩過(guò)的坑. 用來(lái)后續(xù)量大了后,可以提供更好的技術(shù)方法和架構(gòu), 往資深im/im高級(jí)方向發(fā)展, 不僅僅限于xxx項(xiàng)目. 要能夠圍繞整個(gè)IM 領(lǐng)域方向思考

    • 業(yè)界的架構(gòu), 技術(shù)方案, 選型, 都需要先了解.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市浪箭,隨后出現(xiàn)的幾起案子穗椅,更是在濱河造成了極大的恐慌,老刑警劉巖奶栖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件房待,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡驼抹,警方通過(guò)查閱死者的電腦和手機(jī)桑孩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)框冀,“玉大人流椒,你說(shuō)我怎么就攤上這事∶饕玻” “怎么了宣虾?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)温数。 經(jīng)常有香客問(wèn)我绣硝,道長(zhǎng),這世上最難降的妖魔是什么撑刺? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任鹉胖,我火速辦了婚禮,結(jié)果婚禮上够傍,老公的妹妹穿的比我還像新娘甫菠。我一直安慰自己,他們只是感情好冕屯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布寂诱。 她就那樣靜靜地躺著,像睡著了一般安聘。 火紅的嫁衣襯著肌膚如雪痰洒。 梳的紋絲不亂的頭發(fā)上瓢棒,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音丘喻,去河邊找鬼音羞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛仓犬,可吹牛的內(nèi)容都是我干的嗅绰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼搀继,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼窘面!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起叽躯,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤财边,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后点骑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酣难,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年黑滴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了憨募。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡袁辈,死狀恐怖菜谣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晚缩,我是刑警寧澤尾膊,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站荞彼,受9級(jí)特大地震影響冈敛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸣皂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一抓谴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧签夭,春花似錦齐邦、人聲如沸椎侠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)我纪。三九已至慎宾,卻和暖如春丐吓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背趟据。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工券犁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汹碱。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓粘衬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親咳促。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稚新,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容