微信:我們絕不丟消息!
有水友問我說,總感覺微信不丟消息,它是怎么做到的?
之前做過幾十年IM架構(gòu),今天和大家聊聊消息的可靠投遞。
IM系統(tǒng)中,報文分幾種類型?答,三種:
- 請求報文(Request)
- 應(yīng)答報文(Acknowledge)
- 通知報文(Notify)
- R:客戶端主動發(fā)送給服務(wù)器的報文;
- A:服務(wù)器被動應(yīng)答客戶端的報文,一個A對應(yīng)一個R;
- N:服務(wù)器主動發(fā)送給客戶端的報文;
路人架構(gòu)師如何設(shè)計消息投遞流程?
一個沒做過IM系統(tǒng)的路人架構(gòu)師,他可能會這么設(shè)計消息投遞流程:用戶A給用戶B發(fā)送“你好”:
- client-A向im-server發(fā)送msg:R;
- im-server回復(fù)client-A一個msg:A;
- 如果此時client-B在線,則im-server向client-B發(fā)送msg:N;
畫外音:如果client-B不在線,im-server存儲離線消息。
上述消息投遞流程存在什么問題?
流程圖中會發(fā)現(xiàn),發(fā)送方client-A收到msg:A后,只能說明im-server成功接收到了消息,并不能說明client-B接收到了消息。
在若干場景下,可能出現(xiàn)msg:N包丟失,例如:
- 服務(wù)器崩潰,msg:N包未發(fā)出;
- 網(wǎng)絡(luò)抖動,msg:N包被網(wǎng)絡(luò)設(shè)備丟棄;
- client-B崩潰,msg:N包未接收;
結(jié)論是悲觀的:接收方client-B是否有收到msg:N,發(fā)送方client-A完全不可控。
那怎么辦呢?
應(yīng)用層的消息可靠投遞,必須通過應(yīng)用層的超時、重傳、確認來保證。
首先,加入應(yīng)用層的確認機制。
要想讓發(fā)送方client-A確保接收方client-B收到了消息,必須讓接收方client-B回復(fù)client-A一個消息的確認。這個應(yīng)用層的確認流程,與消息的發(fā)送流程類似:
- client-B向im-server發(fā)送ack:R;
- im-server回復(fù)client-B一個ack:A;
- im-server向client-A發(fā)送ack:N;
至此,發(fā)送“你好”的client-A,在收到了ack:N報文后,才能確認client-B真正接收到了“你好”。
你會發(fā)現(xiàn),一條“你好”的發(fā)送,分別包含上下兩個半場,即msg的R/A/N三個報文,ack的R/A/N三個報文,這是IM系統(tǒng)中消息投遞的核心。
還可能存在什么問題?
復(fù)雜的網(wǎng)絡(luò)環(huán)境下,msg:N,ack:N這兩個報文都可能丟失(服務(wù)器奔潰、網(wǎng)絡(luò)抖動、客戶端奔潰),此時client-A都收不到期待的ack:N報文,即client-A不能確認client-B是否收到“你好”,但這兩個報文的丟失對應(yīng)的業(yè)務(wù)影響又大有不同:
- msg:N包丟失,業(yè)務(wù)結(jié)果是client-B沒有收到消息;
- ack:N包丟失,業(yè)務(wù)結(jié)果是client-B收到了消息,只是client-A不知道而已;
結(jié)論仍然是悲觀的:client-A無法知曉具體是哪種情況。
那怎么辦呢?
接下來,要加入超時與重傳。
client-A發(fā)出了msg:R,收到了msg:A之后,在一個期待的時間內(nèi),如果沒有收到ack:N,client-A會嘗試將msg:R重發(fā)。
可能client-A同時發(fā)出了很多消息,故client-A需要在本地維護一個等待ack隊列,并配合timer超時機制,來記錄哪些消息沒有收到ack:N,以定時重發(fā)。
一旦收到了ack:N,說明client-B收到了“你好”消息,對應(yīng)的消息將從“等待ack隊列”中移除。
消息的重傳會引入什么新的問題?
超時與重傳機制可能導(dǎo)致client-B收到重復(fù)的消息。
那怎么辦呢?
最后,client-B要引入消息的去重機制。
發(fā)送方client-A生成一個消息去重的msgid,保存在“等待ack隊列”里,同一條消息使用相同的msgid來重傳,供client-B去重,而不影響用戶體驗。
也就是說,系統(tǒng)層面,client-B其實收到了很多消息,而產(chǎn)品體驗層面,用戶并不知道。系統(tǒng),就是在背后默默保證你體驗的那個人!
總結(jié)
- IM系統(tǒng)通過超時、重傳、確認、去重的機制來保證消息的可靠投遞;
- 一個“你好”的發(fā)送,包含上半場msg:R/A/N與下半場ack:R/A/N的6個報文;
- 等待ACK隊列,是超時重傳的關(guān)鍵,重傳的消息msgid相同;
- 無法做到系統(tǒng)層面的不丟不重,只能做到業(yè)務(wù)層面的不丟不重;
知其然,知其所以然。
思路比結(jié)論更重要。