消息投递失败的第1种情况——用户A发送消息到IM服务器
可能由于网络不通,等待超时等原因造成失败。可以进行消息重发的方式进行弥补,但是有可能导致发送重复消息,因为可能实际上,IM可能已经正确处理了这条消息,只是响应慢了,因此客户端要对同一条消息生成一个唯一的ID,便于服务端去重。
发送消息的唯一ID可以是发送方ID+接收方ID+时间戳组成,本人也觉得客户A发送消息到IM,IM处理完成之后,应该回传ACK给客户A,以便客户A确认消息已经发送成功。跟TCP的ACK不同,TCP的ACK表示网络层消息是否送达,业务层ACK才是真正的表示消息是否处理或送达
消息投递失败的第2中情况——IM推送消息给用户B
课程中用的方式是推送消息到用户B了之后,用户B回送一条签收消息,IM进行消息签收状态更改。但有可能用户B实际上已经不可达了,IM还没感知到,导致消息其实不可达。可以做出的改善是推送消息之前,先把消息加入到一个等待ACK消息列表,一定时间内如果没收到客户B的ACK应答,从等待ACK消息列表中取出消息,尝试重新投递。
当然也有可能是用户B的ACK消息丢了,实际上已经收到消息了,这时候IM如果重复投递消息,会导致用户B重复消息,不过消息去重很好解决,可以根据消息ID进行去重。
还有一种可能性是在推送的时候,IM本身出了问题。那么只能是用户B在重新上戏的时候,让服务端进行完整性检查,重新同步用户B已丢失的数据。
本人了解到的机制有
时间戳比对,即
IM服务器推送消息给客户B时,带上一个时间戳,用户B每次接收到消息时,都更新本地最新消息时间戳为这个时间戳。
假设当前IM正要推送第10条消息,时间戳为timestamp10,结果IM挂了,导致用户B没有收到。
IM恢复后,用户B重新上线,此时携带其本地最新时间戳timestamp6,IM将所有消息时间戳大于timestamp6的消息都返回给用户B,其中就包括时间戳为timestamp10的第10条消息。
用户B收到第10条消息后,更新本地时间戳为timestamp10
当然这个方案有个缺点,就是时钟回拨,有可能存在一定偏差,导致不够精确
说了这么多,其实核心思想就是ACK+超时重传+去重+消息完整性检查这几个手段来确保消息投递可靠性。因为这个比较复杂,所以花了比较大的篇幅来描述,有哪里表达不清晰的,还请老师多包涵。请问老师对此有什么见解?谢谢老师
登录后可查看更多问答,登录/注册
SpringBoot/Netty+MUI全栈开发 同时搞定后台+ Android&iOS
了解课程