当前位置: 首页 > 科技观察

为什么消息排序如此困难?

时间:2023-03-15 19:29:26 科技观察

很多业务需要考虑消息传递的顺序:单聊消息传递,保证发送方和接收方的发送顺序一致。群聊消息传递,确保所有接收者以相同的顺序显示。充值和支付消息保证是同一个用户发起的请求在服务器端以相同的顺序执行。1、消息的顺序是分布式系统架构设计中一个非常难的问题。常见的优化实践有哪些?权衡1:以客户端或服务器的时间为准。需要一个尺子来衡量计时的先后顺序,根据业务场景可以以客户端的时间为准,也可以以服务器的时间为准,例如:邮件显示的顺序其实是以客户端的发送时间为准。如果时间调到1970或2970,收件人收到邮件后可以“顶”或“底”。秒杀活动时间的判断必须以服务器的时间为准。客户端是不可能修改本地时间的,所以可以提前秒杀。权衡2:服务器生成一个单调递增的id作为计时依据。db的seq/auto_inc_id生成一个单调递增的id来保证顺序性。画外音:这个id生成的单点很容易成为瓶颈。妥协三:如果业务能够接受增量id消息发送、post发布时间,甚至秒杀时间的趋势,就没有这种精准的定时要求:1s内发布聊天消息的时间是乱序的,没有什么是与1s内发布的帖子相同排序错误。1s内发起的秒杀,用一下就可以了。由于多台服务器之间的时间误差,登陆A服务器的秒杀成功了,但是登陆B服务器的秒杀还没有开始。在业务上也是可以接受的(用户感知不好)至)因此,对于大多数业务而言,长期趋势上升的时机可以满足业务需求,极短时间的时机误差在一定程度上是可以接受的.因此,始终可以通过分布式id生成算法生成id作为时序依据。Tradeoff4:使用单点序列化可以保证多台机器上的时序数据相同。为了保证高可用性,需要实现数据冗余。相同的数据存储在多个地方。如何保证这些数据的修改消息是一致的呢?“单点序列化”是可行的:先将一台机器上的操作序列化,然后将操作顺序分发到所有机器上,保证多台机器的操作顺序一致,最终数据一致。典型场景一:数据库在主从同步数据库的主从架构中,上游分别发起op1、op2、op3三个操作,主库master序列化所有sql写操作op3、op1、op2,然后将相同的序列发送给从库slave执行,保证所有数据库数据的一致性就是使用了“单点序列化”的思想。典型场景二:GFS中文件的一致性为了保证文件的可用性,GFS(GoogleFileSystem)必须存储一个文件的多个副本。当多个upstream写入同一个文件时,也是由一个masterchunk-server先将写入操作序列化,然后将序列化后的操作发送给其他chunk-server,以保证冗余文件的数据一致性。2、一对一聊天,如何保证发送顺序和接收顺序一致?对于单人聊天,发送方A依次向接收方B发送3条消息msg1、msg2、msg3。这三个消息能否保证显示时序?一致性(发送和显示的顺序相同)?方案的设计思路如下:如果使用服务端的单点序列化时序,可能会出现服务端收到消息的时序是msg3,msg1,msg2,会和发送不一致顺序。业务不需要全局消息一致性。同一个发送者A只需要以相同的时间顺序从ta向B发送消息即可。一种常见的优化方案是在A发送给B的消息中加入一个发送方A的绝对本地时间序列来表示接收方B的呈现时间。msg1{sender:A,seq:10,receiver:B,msg:content1}msg2{sender:A,seq:20,receiver:B,msg:content2}msg3{sender:A,seq:30,receiver:B,msg:content3}可能有问题:如果接收者B先收到msg3,msg3会先显示,收到msg1和msg2后,会显示在msg3的前面。3.对于群聊消息,如何保证所有接收者按相同的顺序接收?对于群聊消息,N个群友在一个群里聊天,如何保证所有群友收到的消息按相同的顺序显示?设计思路如下:假设和单条聊天消息一样,使用sender的seq来保证时序,因为sender不是单点,seq不能统一生成,可能有不一致。因此,可以利用服务器的单点进行序列化。如上图所示,此时群聊的发送流程为:sender1发送msg1,sender2发送msg2;msg1和msg2连接到集群,为集群服务;服务层从底层得到一个***seq来决定接收端的显示时序;service获取msg2的seq为20,msg1的seq为30;通过delivery服务向多个群友发送消息,即使群友在不同时间收到msg1和msg2,也可以按照seq显示;这个方法可以实现,所有群友的消息显示时间是一样的。缺点是生成全局递增序列号的服务很容易成为系统瓶颈。4、有没有更进一步的优化方法?其实群消息不需要保证全局消息顺序的顺序,只需要保证一个群内消息的顺序即可。在这种情况下,“id序列化”就变成了一个非常好的主意。在这个方案中,服务层不再需要到统一的后端去获取全局seq,而是在服务连接池层面做小改动,保证来自一个group的消息落在同一个服务上,这个服务可以使用本地的seq来序列化同一组的所有消息,以确保所有组成员看到消息的时序相同。这时候使用本地时钟生成seq就可以了,是不是很聪明呢?5、总结“有序”,必须有一把尺子来衡量“有序”,可以是客户端的尺子,也可以是服务端的尺子;大部分商家可以接受大范围的趋势单和小范围的误差;绝对有序的业务可以依赖服务器的绝对定时能力;单点序列化是保证多机统一定时的常用方法。典型场景包括db的主从一致,gfs的多个文件一致;一对一聊天,只需要保证发送和接收的时机一致,就可以使用客户端seq;对于群聊,只需要保证所有接收者消息的时序一致,就需要使用服务端seq,有两种方式,一种是单点绝对时序,一种是id序列化;思路比结论更重要,希望大家有所收获。【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】点此阅读更多该作者好文