1. 前言
相信有不少小伙伴们都在工作中使用过MQ进行消息异步推送了,在学习任何一款开源MQ过程中,都会有一个绕不开的话题,消息的可靠性、顺序性,还有唯一性如何保证?
- 可靠性:只要消息推送接口调用成功,用户最终一定能够收到消息。
- 顺序性:保证用户收到的消息跟推送时是一致的,不会发生乱序。
- 唯一性:也称幂等性。保证用户不会收到重复的消息。
盒子IM作为一款优秀的开源产品(自卖自夸),当然也是要支持这些特性。
2. 可靠性
从图中可以看到,一条消息从发送方到接收方,需要经过5个环节。
如果步骤1,2失败,接口会抛出异常,用户得知消息发送失败,不属于消息丢失。
如果步骤3失败,一般是redis挂了导致,等redis重启后,会继续从原队列中拉取消息,消息不会丢失。
如果步骤4失败,有两种可能性:
1.用户掉线导致,此时前端的maxId不会更新,待用户重新登陆,或者断线重连成功时,会根据maxId重新拉取之前失败的消息。
2.im-server宕机导致,此时用户的ws也会断开,并不断地尝试重连。当im-server起来后,用户ws会重连成功,然后会根据maxId重新拉取未收到的消息。
如果步骤5失败,原因是消息到了前端,但未处理完成用户就刷新或者退出了页面。这种情况同样会在用户登陆后,会根据maxId重新拉取到这条消息。
对于步骤4,有小伙伴担心出现一种情况,im-server从队列中拉取了2,3,4号消息,2号消息推送失败,3,4号却推送成功了,此时maxId更新为4, 用户将无法收取2号消息?
其实不会有这种情况。因为ws是基于tcp实现的应用层协议,具有可靠性传输的特性。如果2号消息没有推送成功,后面的消息也不会正常推送。
如果是网络拥塞导致推送失败,会等待网络好转时继续推送。
如果是网络连接断开导致推送失败,此时这个channel已经失效,通过此channel推送任何消息都是抛异常。此时前端也会重新发起连接,重连成功后并拉取离线消息,通过新的channel推送消息。
3. 顺序性
消息的有序性分为全局有序和局部有序。
全局有序:保证整个系统的所有消息都是有序的,实现比较简单粗暴,通常是将消息放到一个队列里面。但吞吐量比较低,非必要不建议使用。
局部有序:对于IM系统来说,局部有序可以理解为保证单一聊天会话内的消息有序即可。实现方式是在业务层控制相同会话的消息进入相同的队列。局部有序的方案可获得更高的吞吐量。
盒子IM采用的局部有序的方案,即保证单一会话内的消息有序
步骤1,2: 出现:“后发先至”情况,即用户后面发的消息被服务器先处理了。
前端发送消息的请求先放入一个先进先出的队列中,只有当前面的消息发送完成后,才发送下一条消息
步骤3.4: im-server从redis拉取到2,3,4号消息,没有按照顺序推送
1.同一个会话的消息会进入相同的队列,保证了从redis取出的消息是有序的。
2.而且必须取出来的消息完成发送后,才会去拉取下一批消息,保证了不会在多个线程同时推送消息
步骤5:有一种极端情况,当”张三”上线时,拉取id为100-200的离线消息,当推送到150号消息时,“李四”发来了201号消息,此时消息顺序变成100…150,201,151…200
是会有这种情况,所以还需要在客户端做最后一层保证。即根据消息id大小进行判断,如果接受到的消息id<maxId,则需要将消息插入到会话中合适的位置,而不是直接插入到最后
暂无评论内容