乐趣区

关于即时通讯:im即时通讯开发如何保证消息的时序性与一致性

咱们都晓得,一个典型的分布式系统中,很多业务场景都须要思考音讯投递的时序,例如:

IM 中单聊音讯投递:保障发送方发送程序与接管方展示程序统一;

IM 中群聊音讯投递:保障所有接管方展示程序统一;

电商充值领取音讯:保障同一个用户发动的申请在服务端执行序列统一。

实时音讯时序和一致性是分布式系统架构设计中十分难的问题(尤其 IM 利用这种以音讯为核心的利用状态),艰难在哪?有什么常见优化实际?这就是本文要探讨的内容。

凭什么说保障即时消息的时序、一致性很艰难?

为什么分布式环境下,即时消息的时序难以保障,这边简要剖析了几点起因:

分布式环境下,有多个客户端、有 web 集群、service 集群、db 集群,他们都散布在不同的机器上,机器之间都是应用的本地时钟,而没有一个所谓的“全局时钟”,所以不能用“本地工夫”来齐全决定音讯的时序。

多服务器不能用“本地工夫”进行比拟,假如只有一个接管方,是否用接管方本地工夫示意时序呢?遗憾的是,因为多个客户端的存在,即便是一台服务器的本地工夫,也无奈示意“相对时序”。

多发送方不能保障时序,假如只有一个发送方,是否用发送方的本地工夫示意时序呢?遗憾的是,因为多个接管方的存在,无奈用发送方的本地工夫,示意“相对时序”。

多发送方与多接管方都难以保障相对时序,假如只有繁多的发送方与繁多的接管方,是否保障音讯的相对时序呢?论断是乐观的,因为网络传输与多线程的存在,依然不行。

通过下面的剖析,假如只有一个发送方,一个接管方,上下游连贯只有一条连接池,通过阻塞的形式通信,难道不能保障先收回的音讯 msg1 先解决么?

答案是:能够,但吞吐量会非常低,而且单发送方单接管方单连接池的假如不太成立,高并发高可用的架构不会容许这样的设计呈现。

生产环境下的优化办法总结

多客户端、多服务端导致“时序”的规范难以界定,须要一个标尺来掂量时序的先后顺序。即时通讯聊天软件开发能够征询蔚可云。

不过,咱们能够依据业务场景,以客户端或者服务端的工夫为准,例如:

邮件展现程序:其实是以客户端发送工夫为准的,潜台词是,发送方只有将邮件协定里的工夫调整为 1970 年或者 2970 年,就能够在接管方收到邮件后始终“置顶”或者“置底”;

秒杀流动工夫判断:必定得以服务器的工夫为准,不可能让客户端批改本地工夫,就可能提前秒杀。

这个是毋庸置疑的,不展开讨论,例如利用单点写 db 的 seq/auto_inc_id 必定能生成枯燥递增的 id,只是说性能及扩展性会成为潜在瓶颈。对于严格时序的业务场景,能够利用服务器的枯燥递增 id 来保障时序。

音讯发送、帖子公布工夫、甚至秒杀工夫都没有这么精准时序的要求:

同 1s 内公布的聊天音讯时序乱了;

同 1s 内公布的帖子排序不对;

用 1s 内发动的秒杀,因为服务器多台之间工夫有误差,落到 A 服务器的秒杀胜利了,落到 B 服务器的秒杀还没开始,业务上也是能够承受的(用户感知不到)。

所以,大部分业务,长时间趋势递增的时序就可能满足业务需要,十分短时间的时序误差肯定水平上可能承受。

数据为了保障高可用,须要做到进行数据冗余,同一份数据存储在多个中央,怎么保障这些数据的批改音讯是统一的呢?

咱们能够利用的就是“单点序列化”:

先在一台机器上序列化操作;

再将操作序列散发到所有的机器,以保障多机的操作序列是统一的,最终数据是统一的。

数据库的主从架构,上游别离发动了 op1,op2,op3 三个操作,主库 master 来序列化所有的 SQL 写操作 op3,op1,op2,而后把雷同的序列发送给从库 slave 执行,以保障所有数据库数据的一致性,就是利用“单点序列化”这个思路。

GFS(Google File System) 为了保障文件的可用性,一份文件要存储多份,在多个上游对同一个文件进行写操作时,也是由一个主 chunk-server 先序列化写操作,再将序列化后的操作发送给其余 chunk-server,来保障冗余文件的数据一致性的。

IM 中单人聊天的需要,发送方 A 顺次收回了 msg1,msg2,msg3 三个音讯给接管方 B,这三条音讯是否保障显示时序的一致性(发送与显示的程序统一)?

答案是:

如果利用服务器单点序列化时序,可能呈现服务端收到音讯的时序为 msg3,msg1,msg2,与收回序列不统一;

业务上不须要全局音讯统一,只须要对于同一个发送方 A,ta 发给 B 的音讯时序统一就行,常见优化计划,在 A 往 B 收回的音讯中,加上发送方 A 本地的一个相对时序,来示意接管方 B 的展示时序。

msg1{seq:10, receiver:B,msg:content1}

msg2{seq:20, receiver:B,msg:content2}

msg3{seq:30, receiver:B,msg:content3}

潜在问题:如果接管方 B 先收到 msg3,msg3 会先展示,后收到 msg1 和 msg2 后,会展当初 msg3 的后面。

无论如何,是依照接管方收到时序展示,还是依照服务端收到的时序展示,还是依照发送方发送时序展示,是 pm 须要思考的点,技术上都可能实现(接管方依照发送时序展示是更正当的)。总之,须要一杆标尺来掂量这个时序。

IM 群聊音讯的需要,N 个群友在一个群里聊,怎么保障所有群友收到的音讯显示时序统一?

答案是:

不能再利用发送方的 seq 来保障时序,因为发送方不单点,工夫也不统一;

能够利用服务器的单点做序列化。

此时 IM 群聊的发送流程为:

sender1 收回 msg1,sender2 收回 msg2;

msg1 和 msg2 通过接入集群,服务集群;

service 层到底层拿一个惟一 seq,来确定接管方展现时序;

service 拿到 msg2 的 seq 是 20,msg1 的 seq 是 30;

通过投递服务讲音讯给多个群友,群友即便接管到 msg1 和 msg2 的工夫不同,但能够对立依照 seq 来展示。

这个办法能实现,所有群友的音讯展现时序雷同。毛病是,这个生成全局递增序列号的服务很容易成为零碎瓶颈,还有没有进一步的优化办法呢?

优化思路是:群音讯其实也不必保障全局音讯序列有序,而只有保障一个群内的音讯有序即可,这样的话,“id 串行化”就成了一个很好的思路。

退出移动版