微信:我绝不丢离线消息!(第54讲)

《架构师之路:架构设计中的100个知识点》

54.离线消息可靠投递

微信:我们绝不丢消息!》提到,单人实时聊天消息的可靠投递,是通过应用层的超时、重传、确认、去重来保证的。

那如果没有打开手机,没有登录微信,好友发给我的微信消息,有没有可能丢失呢?今天和大家聊聊离线消息的话题。

没有做过IM业务的架构师可能会说,离线消息存储数据库不就行了吗?可事实上,远比你想的复杂。

接收方不在线,消息发送流程是怎么样的?

图片
如上图所述,A给B发了一条消息,而B不在线,离线消息存储的流程如下:

1. A发送消息给B,通过server中转;

2. server查看用户B的状态为offline;

3. server将消息存储到DB中;

4. server返回用户A发送成功,并带上特殊标识,避免A重发;

离线消息表如何设计?

很容易想到,消息业务有这样的一些关键属性

t_offline_msg(  receiver_uid,  // 离线消息接收方  msg_id,  // 消息ID  time,  // 消息发送时间  sender_uid, // 消息发送方  msg_type, // 消息类型  msg_content, // 消息内容   …);

B登陆之后,如何拉取A发给他的离线消息呢?

(receiver_uid(B), sender_uid(A))

在上述索引查询,然后把离线消息删除,再把消息返回B即可。

图片
整体流程如上图所述:

1. B拉取A发送给ta的离线消息;

2. server从DB中拉取离线消息;

3. server从DB中把离线消息删除;

4. server返回给用户B想要的离线消息;

想到这一步,也不难。

那么问题来了,B登录微信的时候,不止要拉取A发给他的离线消息,还需要拉取所有其他好友发给他的离线消息,这该如何实现呢?

如果用户B有很多好友,登录后客户端需要对所有好友进行离线消息拉取。

客户端伪代码:

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

如果有10000个好友,难道要拉取10000次?

画外音:我的微信好友已满员,大家猜微信好友上限是多少?


有没有减少拉取次数的优化方法呢?

按需拉取:先拉取各个好友的离线消息数量,真正查看离线消息时,才往服务器发送拉取请求。

除了减少流量的“按需拉取”优化,还有减少拉取次数的优化方案么?

图片
一次性拉取:可以一次性通过receiver_uid即接收方ID,拉取所有好友发送给B的离线消息,把登录时与服务器的交互次数降低为了1次。到客户端本地再根据sender_uid进行计算。

问题又来了,用户B一次性拉取所有好友发给ta的离线消息,消息量很大时,一个请求包很大,速度慢怎么办?

图片
分页拉取:根据业务需求,先拉取最新的一页消息,再按需一页页拉取。

新的问题又来了,离线消息会不会丢失,用户会不会收不到呢?

例如,上述步骤第三步执行完毕之后(删除了离线消息),第四个步骤离线消息返回给客户端过程中,服务器挂掉,路由器丢消息,或者客户端crash了,那离线消息岂不是丢了么。

画外音:数据库已删除,用户却还没看到。

如何保证离线消息的可达性?

图片
加入ACK机制:如同在线消息的应用层ACK机制一样,离线消息拉时,不能够直接删除数据库中的离线消息,而必须等应用层的离线消息ACK,等客户端真的收到离线消息,才能删除数据库中的离线消息。

新的问题又来了,如果用户B拉取了一页离线消息,却在ACK之前crash了,下次登录时会拉取到重复的离线消息么?

拉取了离线消息却没有ACK,服务器不会删除之前的离线消息,故下次登录时系统层面还会拉取到。和在线消息投递一样,接收方通过msgid去重,系统层面会收到重复消息,但在业务层面,用户却无感知。

图片
另一个问题,假设有N页离线消息,现在每个离线消息需要一个ACK,那么岂不是客户端与服务器的交互次数又加倍了?有没有优化空间?

图片
其实,不用每一页消息都ACK,在拉取第二页消息时相当于第一页消息的ACK,此时服务器再删除第一页的离线消息即可,最后一页消息再ACK一次。这样的效果是,不管拉取多少页离线消息,只会多一个ACK请求,与服务器多一次交互

稍作总结

离线消息的可靠投递,关键技术有:

1. 对于同一个用户B,一次性拉取所有用户发给ta的离线消息,能大大减少服务器交互次数;

2. 按需拉取,是无线端的常见优化;

3. 分页拉取,是一个请求次数与包大小的折衷;

4. 应用层的ACK,应用层的去重,才能保证离线消息的不丢不重;

5. 下一页的拉取,同时作为上一页的ACK,能够极大减少与服务器的交互次数;

知其然,知其所以然。

思路比结论更重要。

==全文完==


微信:我们绝不丢消息!》中有人喷我,说:“你压根没做过IM,就在这瞎忽悠”。我可能说的不完全对,但我真的做过几十年IM。不信你看:《关于即时通讯架构的一切!

另外,这篇文章大伙帮我转一下:

40岁,我创业了,一个人...

一个真诚的技术人创业了,大家多多支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值