我们都知道,一个典型的分布式系统中,很多业务场景都需要考虑消息投递的时序,例如:
IM中单聊消息投递:保证发送方发送顺序与接收方展现顺序一致;IM中群聊消息投递:保证所有接收方展现顺序一致;电商充值支付消息:保证同一个用户发起的请求在服务端执行序列一致。
实时消息时序和一致性是分布式系统架构设计中非常难的问题(尤其IM应用这种以消息为中心的应用形态),困难在哪?有什么常见优化实践?这就是本文要讨论的内容。
凭什么说保证即时消息的时序、一致性很困难?
为什么分布式环境下,即时消息的时序难以保证,这边简要分析了几点原因:
1时钟不一致
分布式环境下,有多个客户端、有web集群、service集群、db集群,他们都分布在不同的机器上,机器之间都是使用的本地时钟,而没有一个所谓的“全局时钟”,所以不能用“本地时间”来完全决定消息的时序。
2多客户端(发送方)
多服务器不能用“本地时间”进行比较,假设只有一个接收方,能否用接收方本地时间表示时序呢?遗憾的是,由于多个客户端的存在,即使是一台服务器的本地时间,也无法表示“绝对时序”。
如上图,绝对时序上,APP1先发出msg1,APP2后发出msg2,都发往服务器web1,网络传输是不能保证msg1一定先于msg2到达的,所以即使以一台服务器web1的时间为准,也不能精准描述msg1与msg2的绝对时序。
3服务集群(多接收方)
多发送方不能保证时序,假设只有一个发送方,能否用发送方的本地时间表示时序呢?遗憾的是,由于多个接收方的存在,无法用发送方的本地时间,表示“绝对时序”。
如上图,绝对时序上,web1先发出msg1,后发出msg2,由于网络传输及多接收方的存在,无法保证msg1先被接收到先被处理,故也无法保证msg1与msg2的处理时序。
4网络传输与多线程
多发送方与多接收方都难以保证绝对时序,假设只有单一的发送方与单一的接收方,能否保证消息的绝对时序呢?结论是悲观的,由于网络传输与多线程的存在,仍然不行。
如上图,web1先发出msg1,后发出msg2,即使msg1先到达(网络传输其实还不能保证msg1先到达),由于多线程的存在,也不能保证msg1先被处理完。
5怎么保证绝对时序
通过上面的分析,假设只有一个发送方,一个接收方,上下游连接只有一条连接池,通过阻塞的方式通讯,难道不能保证先发出的消息msg1先处理么?
答案是:可以,但吞吐量会非常低,而且单发送方单接收方单连接池的假设不太成立,高并发高可用的架构不会允许这样的设计出现。即时通讯聊天软件app开发可以加小蓝豆的v:weikeyun24咨询
生产环境下的优化方法总结
1以客户端或者服务端的时序为准
多客户端、多服务端导致“时序”的标准难以界定,需要一个标尺来衡量时序的先后顺序。
不过,我们可以根据业务场景,以客户端或者服务端的时间为准,例如:
邮件展示顺序:其实是以客户端发送时间为准的,潜台词是,发送方只要将邮件协议里的时间调整为1970年或者2970年,就可以在接收方收到邮件后一直“置顶”或者“置底”;秒杀活动时间判断:肯定得以服务器的时间为准,不可能让客户端修改本地时间,就能够提前秒杀。
2服务端能够生成单调递增的id
这个是毋庸置疑的,不展开讨论,例如利用单点写db的seq/auto_inc_id肯定能生成单调递增的id,只是说性能及扩展性会成为潜在瓶颈。对于严格时序的业务场景,可以利用服务器的单调递增id来保证时序。
3大部分业务能接受误差不大的趋势递增id
消息发送、帖子发布时间、甚至秒杀时间都没有这么精准时序的要求:
同1s内发布的聊天消息时序乱了;同1s内发布的帖子排序不对;用1s内发起的秒杀,由于服务器多台之间时间有误差,落到A服务器的秒杀成功了,落到B服务器的秒杀还没开始,业务上也是可以接受的(用户感知不到)。
所以,大部分业务,长时间趋势递增的时序就能够满足业务需求,非常短时间的时序误差一定程度上能够接受。关于绝对递增id,趋势递增id的生成架构,详见文章《细聊分布式ID生成方法》,此处不展开。