开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情
我们知道TCP是可以保证消息的可靠性传递,但不保证业务层的可靠性。如果业务处理异常,那消息就会丢失,所以需要在业务层实现一个ACK机制保证消息可靠性传递。
下图是IM消息在系统中的流转:
上图5个步骤中都需要保证可靠性
- 用户A发送消息,长连接服务调用gRpc成功后返回ACK确认通知用户A,完成可靠性传递。如果收不到服务的ACK则进行重试。
- gRpc将消息转发给业务服务中的MQ,发送消息到MQ利用MQ的特性保证消息一定传递到MQ中。
- 同上
- gRpc利用异步回调+重试方式保证消息投递到长连接层服务。
- 长连接层投递消息到用户B时,实现ACK机制保证可靠性。当消息投递出去时同时把消息加入到内存维护的待确认消息队列,当用户B返回确认ACK时从待确认消息中移除该消息。
以上逻辑似乎还不能完全保证消息的可靠性,比如第5中的ACK机制,如果此时服务宕机或客户端断线重连,这个机制就会失效了,这样发送给用户B的消息并不能保证可靠性传递。所以还需要客户端重连时主动拉取消息。
客户端建立连接成功后,会先拉取离线消息或未确认消息。这部分消息会存到用户的消息信箱中。用户拉取离线消息究竟拉取哪些消息?这就需要客户端有一个标识来确定拉取的范围。一般做法是客户端每次成功接收最新的消息后会保存该条最新消息的时间戳+消息ID,重连后会根据这2个字段作为查询条件拉取未接收的新消息。拉取消息的方式可以是HTTP或TCP推送,如果消息量很大还需要配合将消息压缩打包后发送给客户端。
弱网条件下如何保证消息的实时性和可靠性?
我们使用微信在地铁或电梯都遇到一个场景,能接收到信息但发送不出去。这是因为在弱网环境下移动网络可能掉回2/3G,这时候TCP连接就变得不稳定,甚至连接不上。这时候发送消息失败可以理解。那为什么能接收消息呢?这里有一个解决的方案就是,客户端除了依靠TCP方式接收消息外还会使用HTTP主动拉取。根据实际合理设置1/3/5分钟主动发起HTTP向服务端拉取一次消息,通过组合方式提高可靠性传递。