IM如何保证消息不乱序

对于聊天、直播互动等业务来说,消息的时序代表的是发送方的意见表述和接收方的语义逻辑理解,如果时序一致性不能保证,科能就i造成聊天语义不连贯,容易出现曲解和误会。
对于点对点的聊天场景,时序一致性需要保证接收方的接收顺序和发送方的发出顺序一致;而对于群组聊天,时序一致性保证的是群里所有接收人看到的消息展现顺序都一样。

从理论上来说,保持消息的时序一致性不难,理论上,我们想象的消息收发场景中,只有单一的发送方,单一的接收方。

如果发送方和接收方的消息都是单线程操作,并且和IM服务端都只有唯一的一个TCP连接,来进行消息传输,IM服务端也只有一个线程来处理消息接收和消息推送。这种场景下,消息的时序一致性是比较容易能得到保障的。

但在实际的后端工程实现上,由于单发送方,单接收方,单处理线程的模型吞吐量和效率都太低。更多的场景下,我们可能需要面对的多发送方,多接收方,服务端多线程并发处理的情况。
消息的时序一致性其实是要求我们的消息具备“时序可比较性”,也就是消息相对某一个共同的“时序基准”可以进行比较,所以,要保证消息的时序一致性的一个关键问题是:我们能否找到这么一个时序基准。

我们可以分为以下步骤:
1.如何找到时序基准
2.时序基准的可用性问题
3.有了时序基准,还有其他的误差吗?有什么办法可以减少这些误差。

如何找到时序基准?

如果用本地序号和本地时钟是否可以作为“时序基准”?
用本地时间戳或本地维护的一个序号给到IM服务端,IM服务端再把这个时间戳或者序号和消息一起发送给消息接收方,消息接收方根据这个时间戳或者序号来进行消息排序。这种做法有什么缺点?
A.发送方时钟存在较大不稳定因素,用户可以随时调整时钟导致序号回退。
B.发送本地序号如果重装应用会导致序号清零,也会出现回退。
C.类似“群组消息”和“单用户的多点登录” 这种多方发送场景,都存在:同一时钟的某一时间点,可能有多条消息发送给同一接收对象。比如同一个群里,多个人同时发言;

IM服务器的本地时钟是否可以作为“时序基准”?
IM服务器作为时序基准是指:发送方把消息交给IM服务器后,IM服务器依据自身服务器的时钟生成一个时间戳,再把消息推送给接收方时携带这个时间戳,接收方依据这个时间戳来进行消息的排序。

可是在实际工程中,IM服务器都是集群化部署,虽然多台服务器可以通过NTP时间同步服务,能降低服务集群机器间的时钟差异到毫秒级别,但仍然还是存在一定的时钟误差,而且IM服务器规模相对比较大,时钟的同一比较有挑战。

既然单机本地化的时钟或者序号都存在问题,那么如果有一个全局的时钟或者序号是不是就能解决这个问题?所有的消息的排序都依托于这个全局的序号,这样就不存在时钟不同步的问题了。那么,

IM服务端的全局序列是否可能作为“时序基准”?

用Redis的原子自增命令incr,DB自带的自增id,或者类似Twitter的snowflake算法。

但是也有一些问题:Redis的原子自增和DB的自增id,都要求再主库上来执行“取号”操作,而主库基本都是单点部署,可用性上的保障会相对较差,另外,针对高并发的取号操作这个个单点的主库容易出现性能瓶颈。

从业务层面来考虑,对于群聊和多点登录这种场景,没有必要保证全局的跨多个群的绝对时序性,只要保证某一个群的消息有序即可。
如果可以针对每一个群有独立一个“ID生产器”,能通过哈希规则把压力分散到多个主库实例上,大量降低多个群公用一个“ID”生成器的并发压力

时序基准之外的其他误差
有了时序基准,是不是就能保证消息能按照“既定顺序” 到达接收方呢?
不一定:
1.IM服务器都是集群化部署,每台服务器的机器性能存在差异,因此处理效率有差别,并不能保证先到的消息一定可以先推送到接收方,比如有的服务器处理的很慢,或者正好碰到一次GC,导致它接收的更早消息,反而比其他消息更晚推送出去。
2.IM 服务端接收到发送方的消息后,都是多线程处理,比如“取号”“ 暂存消息”等,并不能保证完全一致性。

你可能感兴趣的:(java,redis,IM)