自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

微信:我絕不丟離線消息!

開發(fā)
如果沒有打開手機(jī),沒有登錄微信,好友發(fā)給我的微信消息,有沒有可能丟失呢?今天和大家聊聊離線消息的話題。

微信:我們絕不丟消息!》提到,單人實(shí)時(shí)聊天消息的可靠投遞,是通過應(yīng)用層的超時(shí)、重傳、確認(rèn)、去重來保證的。

那如果沒有打開手機(jī),沒有登錄微信,好友發(fā)給我的微信消息,有沒有可能丟失呢?今天和大家聊聊離線消息的話題。

沒有做過IM業(yè)務(wù)的架構(gòu)師可能會(huì)說,離線消息存儲(chǔ)數(shù)據(jù)庫不就行了嗎?可事實(shí)上,遠(yuǎn)比你想的復(fù)雜。

接收方不在線,消息發(fā)送流程是怎么樣的?

如上圖所述,A給B發(fā)了一條消息,而B不在線,離線消息存儲(chǔ)的流程如下:

1. A發(fā)送消息給B,通過server中轉(zhuǎn);

2. server查看用戶B的狀態(tài)為offline;

3. server將消息存儲(chǔ)到DB中;

4. server返回用戶A發(fā)送成功,并帶上特殊標(biāo)識(shí),避免A重發(fā)。

離線消息表如何設(shè)計(jì)?

很容易想到,消息業(yè)務(wù)有這樣的一些關(guān)鍵屬性:

t_offline_msg(
  receiver_uid,  // 離線消息接收方
  msg_id,  // 消息ID
  time,  // 消息發(fā)送時(shí)間
  sender_uid, // 消息發(fā)送方
  msg_type, // 消息類型
  msg_content, // 消息內(nèi)容 
  …
);

B登陸之后,如何拉取A發(fā)給他的離線消息呢?

(receiver_uid(B), sender_uid(A))

在上述索引查詢,然后把離線消息刪除,再把消息返回B即可。

整體流程如上圖所述:

  • B拉取A發(fā)送給ta的離線消息;
  • server從DB中拉取離線消息;
  • server從DB中把離線消息刪除;
  • server返回給用戶B想要的離線消息;

想到這一步,也不難。

那么問題來了,B登錄微信的時(shí)候,不止要拉取A發(fā)給他的離線消息,還需要拉取所有其他好友發(fā)給他的離線消息,這該如何實(shí)現(xiàn)呢?

如果用戶B有很多好友,登錄后客戶端需要對(duì)所有好友進(jìn)行離線消息拉取。

客戶端偽代碼:

get B's friend-list;  // 拉取B的好友列表
for(all uid in B's friend-list){    // 遍歷所有好友uid
         get_offline_msg(B,uid);   // 拉取離線消息
}

如果有10000個(gè)好友,難道要拉取10000次?

畫外音:我的微信好友已滿員,大家猜微信好友上限是多少?

有沒有減少拉取次數(shù)的優(yōu)化方法呢?

按需拉取:先拉取各個(gè)好友的離線消息數(shù)量,真正查看離線消息時(shí),才往服務(wù)器發(fā)送拉取請(qǐng)求。

除了減少流量的“按需拉取”優(yōu)化,還有減少拉取次數(shù)的優(yōu)化方案么?

一次性拉?。嚎梢砸淮涡酝ㄟ^receiver_uid即接收方ID,拉取所有好友發(fā)送給B的離線消息,把登錄時(shí)與服務(wù)器的交互次數(shù)降低為了1次。到客戶端本地再根據(jù)sender_uid進(jìn)行計(jì)算。

問題又來了,用戶B一次性拉取所有好友發(fā)給ta的離線消息,消息量很大時(shí),一個(gè)請(qǐng)求包很大,速度慢怎么辦?

分頁拉?。焊鶕?jù)業(yè)務(wù)需求,先拉取最新的一頁消息,再按需一頁頁拉取。

新的問題又來了,離線消息會(huì)不會(huì)丟失,用戶會(huì)不會(huì)收不到呢?

例如,上述步驟第三步執(zhí)行完畢之后(刪除了離線消息),第四個(gè)步驟離線消息返回給客戶端過程中,服務(wù)器掛掉,路由器丟消息,或者客戶端crash了,那離線消息豈不是丟了么。

畫外音:數(shù)據(jù)庫已刪除,用戶卻還沒看到。

如何保證離線消息的可達(dá)性?

加入ACK機(jī)制:如同在線消息的應(yīng)用層ACK機(jī)制一樣,離線消息拉時(shí),不能夠直接刪除數(shù)據(jù)庫中的離線消息,而必須等應(yīng)用層的離線消息ACK,等客戶端真的收到離線消息,才能刪除數(shù)據(jù)庫中的離線消息。

新的問題又來了,如果用戶B拉取了一頁離線消息,卻在ACK之前crash了,下次登錄時(shí)會(huì)拉取到重復(fù)的離線消息么?

拉取了離線消息卻沒有ACK,服務(wù)器不會(huì)刪除之前的離線消息,故下次登錄時(shí)系統(tǒng)層面還會(huì)拉取到。和在線消息投遞一樣,接收方通過msgid去重,系統(tǒng)層面會(huì)收到重復(fù)消息,但在業(yè)務(wù)層面,用戶卻無感知。

另一個(gè)問題,假設(shè)有N頁離線消息,現(xiàn)在每個(gè)離線消息需要一個(gè)ACK,那么豈不是客戶端與服務(wù)器的交互次數(shù)又加倍了?有沒有優(yōu)化空間?

其實(shí),不用每一頁消息都ACK,在拉取第二頁消息時(shí)相當(dāng)于第一頁消息的ACK,此時(shí)服務(wù)器再刪除第一頁的離線消息即可,最后一頁消息再ACK一次。這樣的效果是,不管拉取多少頁離線消息,只會(huì)多一個(gè)ACK請(qǐng)求,與服務(wù)器多一次交互。

稍作總結(jié)

離線消息的可靠投遞,關(guān)鍵技術(shù)有:

  • 對(duì)于同一個(gè)用戶B,一次性拉取所有用戶發(fā)給ta的離線消息,能大大減少服務(wù)器交互次數(shù);
  • 按需拉取,是無線端的常見優(yōu)化;
  • 分頁拉取,是一個(gè)請(qǐng)求次數(shù)與包大小的折衷;
  • 應(yīng)用層的ACK,應(yīng)用層的去重,才能保證離線消息的不丟不重;
  • 下一頁的拉取,同時(shí)作為上一頁的ACK,能夠極大減少與服務(wù)器的交互次數(shù);

知其然,知其所以然。

思路比結(jié)論更重要。

責(zé)任編輯:趙寧寧 來源: 架構(gòu)師之路
相關(guān)推薦

2025-03-31 10:49:16

2016-11-02 13:12:31

微信離線消息

2016-10-11 16:31:56

微信服務(wù)器消息

2025-04-17 09:00:00

架構(gòu)聊消息微信

2024-04-09 09:08:09

Kafka消息架構(gòu)

2021-09-08 15:10:02

微信收費(fèi)移動(dòng)應(yīng)用

2019-08-21 07:44:32

離線消息拉取開發(fā)

2013-04-08 16:19:40

微信微信公眾平臺(tái)圖文消息

2024-12-19 10:00:00

Python發(fā)送消息編程

2013-10-24 11:00:30

馬云微信

2021-09-07 08:26:07

微信微信收費(fèi)騰訊

2018-05-23 09:11:42

微信Android開發(fā)面試

2013-08-08 10:13:25

微信

2022-09-12 18:29:49

kafka緩存數(shù)據(jù)

2015-06-04 09:26:23

微信推送模板PHP代碼

2014-09-24 11:32:21

微信企業(yè)號(hào)開發(fā)

2014-09-24 11:11:08

微信企業(yè)號(hào)開發(fā)

2021-04-16 11:27:16

Python表情微信

2021-12-07 18:33:53

Kafka消息集群

2021-05-14 07:18:07

監(jiān)控微信聊天
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)