微信为啥不丢“离线消息”?

开发 开发工具
当发送方用户A发送消息给接收方用户B时,如果用户B在线,之前的文章《微信为啥不丢“在线消息”?》聊过,可以通过应用层的确认,发送方的超时重传,接收方的去重保证业务层面消息的不丢不重。 那如果接收方用户B不在线,系统是如何保证消息的可达性的呢?这是本文要讨论的问题。

需求缘起

当发送方用户A发送消息给接收方用户B时,如果用户B在线,之前的文章《微信为啥不丢“在线消息”?》聊过,可以通过应用层的确认,发送方的超时重传,接收方的去重保证业务层面消息的不丢不重。

那如果接收方用户B不在线,系统是如何保证消息的可达性的呢?这是本文要讨论的问题。

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

回答:如上图所述,

(1)用户A发送消息给用户B

(2)服务器查看用户B的状态为offline

(3)服务器将消息存储到DB中

(4)服务器返回用户A发送成功(对于发送方而言,消息落地DB就认为发送成功)

问题:离线消息表的设计,拉取离线的过程?

receiver_uid, msg_id, time, sender_uid,msg_type, msg_content …

访问模式:接收方B要拉取发送方A给ta发送的离线消息,只需在receiver_uid(B), sender_uid(A)上查询,然后把离线消息删除,再把消息返回B即可。

整体流程如上图所述,

(1)用户B拉取用户A发送给ta的离线消息

(2)服务器从DB中拉取离线消息

(3)服务器从DB中把离线消息删除

(4)服务器返回给用户B想要的离线消息

问题:上述流程存在的问题?

回答:如果用户B有很多好友,登陆时客户端需要对所有好友进行离线消息拉取,客户端与服务器交互次数较多

客户端伪代码:

  1. for(all uid in B’s friend-list){ // 登陆时所有好友都要拉取 
  2.  
  3. get_offline_msg(B,uid); // 与服务器交互 
  4.  

 

优化方案一:先拉取各个好友的离线消息数量,真正用户B进去看离线消息时,才往服务器发送拉取请求(手机端为了节省流量,经常会使用这个按需拉取的优化)

优化方案二:一次性拉取所有好友发送给用户B的离线消息,到客户端本地再根据sender_uid进行计算,这样的话,离校消息表的访问模式就变为->只需要按照receiver_uid来查询了。登录时与服务器的交互次数降低为了1次。

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

回答:分页拉取,根据业务需求,先拉取***(或者最旧)的一页消息,再按需一页页拉取。

问题:如何保证可达性,上述步骤第三步执行完毕之后,第四个步骤离线消息返回给客户端过程中,服务器挂点,路由器丢消息,或者客户端crash了,那离线消息岂不是丢了么(数据库已删除,用户还没收到)?

回答:嗯,如果按照上述的1,2,3,4步流程,的确是的,那如何保证离线消息的可达性?

如同在线消息的应用层ACK机制一样,离线消息拉时,不能够直接删除数据库中的离线消息,而必须等应用层的离线消息ACK(说明用户B真的收到离线消息了),才能删除数据库中的离线消息。

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

回答:拉取了离线消息却没有ACK,服务器不会删除之前的离线消息,故下次登录时系统层面还会拉取到。但在业务层面,可以根据msg_id去重。SMC理论:系统层面无法做到消息不丢不重,业务层面可以做到,对用户无感知。

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

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

总结

“离线消息”的可达性可能比大家想象的要复杂,常见的优化有:

(1)对于同一个用户B,一次性拉取所有用户发给ta的离线消息,再在客户端本地进行发送方分析,相比按照发送方一个个进行消息拉取,能大大减少服务器交互次数

(2)分页拉取,先拉取计数再按需拉取,是无线端的常见优化

(3)应用层的ACK,应用层的去重,才能保证离线消息的不丢不重

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

文章转载自微信公众号“架构师之路”

责任编辑:庞桂玉 来源: 架构师之路
相关推荐

2016-10-11 16:31:56

微信服务器消息

2021-03-08 10:19:59

MQ消息磁盘

2017-10-26 11:36:46

微信撤回消息

2020-10-26 09:19:11

线程池消息

2016-11-10 21:00:49

消息存储数据

2019-08-21 07:44:32

离线消息拉取开发

2013-04-08 16:19:40

微信微信公众平台图文消息

2014-09-24 11:11:08

微信企业号开发

2021-09-08 15:10:02

微信收费移动应用

2013-08-08 10:13:25

微信

2015-06-04 09:26:23

微信推送模板PHP代码

2014-09-24 11:32:21

微信企业号开发

2017-03-17 15:10:52

微信消息撤回功能

2021-01-18 16:19:34

微信系统抖动移动应用

2018-12-14 14:58:04

Python微信消息

2023-12-11 11:16:57

消息模板脚本微信

2018-07-25 13:34:14

Python微信撤回

2013-04-10 18:29:09

微信公众平台接口开发

2013-11-12 23:32:53

微信公号微信公众账号

2020-11-09 09:46:27

MySQLText类型
点赞
收藏

51CTO技术栈公众号