很多业务需要考虑消息传递的顺序:单聊消息传递,保证发送方发送的顺序与接收方显示的顺序一致;群聊消息发送,确保所有接收者以相同的顺序显示;用户发起的请求在服务器端顺序执行;消息序列是分布式系统架构设计中一个非常难的问题。有哪些常见的优化实践?妥协一:基于客户端或服务器的计时不管是什么情况,都需要一把尺子来衡量计时的先后顺序。根据业务场景,以客户端或服务端的时间为准,例如:(1)邮件显示顺序,其实是以客户端的发送时间为准;画外音:只要发件人将邮件协议中的时间调整为1970或2970,收件人收到邮件后就可以一直“顶”或“底”。(2)判断秒杀活动的时间一定要以服务器的时间为准。不能让客户端修改本地时间,这样可以提前秒杀;权衡2:服务器生成一个单调递增的id作为计时依据。对于时序严格的业务场景,可以使用单点写db的seq/auto_inc_id生成一个单调递增的id来保证顺序。画外音:这个id生成的单点很容易成为瓶颈。妥协三:如果业务能接受id消息越来越少的趋势,对id消息的发送、post发布时间,甚至秒杀时间都没有这么精确的时间要求:1s以内的聊天消息发布时间已经过时了点单,??没关系;帖子顺序错了,没关系;1s内发起的秒杀,由于多台服务器之间的时间误差,落到服务器A的秒杀上;如果成功,秒杀落到B服务器还没有启动,业务上也是可以接受的(用户无法感知),所以对于大部分业务来说,长期上升趋势的时机是可以满足业务需求的,并且很短时间的计时误差在一定程度上是可以接受的。因此,可以采用分布式id生成算法生成id作为时序依据。Tradeoff4:使用单点序列化可以保证多台机器上的时序数据相同。为了保证高可用性,需要数据冗余。相同的数据存储在多个地方。如何保证这些数据的修改消息是一致的呢?“单点序列化”是可行的:先在一台机器上序列化操作;然后将操作顺序分发给所有机器,保证多台机器的操作顺序一致,最终数据一致;典型场景一:数据库主从同步数据库的主从架构。上游分别发起op1、op2、op3三个操作。主库master将所有SQL写操作op3、op1、op2序列化,然后将相同的序列发送给从库。从机执行保证所有数据库数据的一致性,就是利用了“单点序列化”的思想。典型场景二:GFS中文件的一致性为了保证文件的可用性,GFS(GoogleFileSystem)必须存储一个文件的多个副本。当多个upstream写入同一个文件时,也是由一个masterchunk-server先将写入操作序列化,然后将序列化后的操作发送给其他chunk-server,以保证冗余文件的数据一致性。在一对一聊天中,如何保证发送顺序和接收顺序一致?针对单人聊天的需求,发送方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的前面。如何保证群聊中所有接收者以相同的顺序接收消息?群聊消息的需求,N个群友在一个群里聊天,如何保证所有群友收到的消息按相同的顺序展示?该方案的设计思路如下:假设和单条聊天消息一样,使用sender的seq来保证时序,因为sender不是单点,seq不能统一生成,并且可能会有不一致。因此,可以利用服务器的单点进行序列化。如上图所示,此时群聊的发送流程为:sender1发送msg1,sender2发送msg2;msg1和msg2连接到集群,为集群服务;服务层从底层获取一个唯一的seq来确定接收端的显示时序;服务获取msg2的seq为20,msg1的seq为30;通过投递服务,将消息发送给多个群友。即使群友在不同时间收到msg1和msg2,也可以按照seq显示;该方法可以实现,所有群成员好友的消息显示时间相同。缺点是生成全局递增序列号的服务很容易成为系统瓶颈。有没有更进一步的优化方法?其实群消息不需要保证全局的消息顺序是有序的,只需要保证一个群内的消息是有序的即可。在这种情况下,“id序列化”就成了一个好主意。在这个方案中,服务层不再需要到统一的后端去获取全局seq,而是在服务连接池层面做小改动,保证来自一个group的消息落在同一个服务上,这个服务可以使用本地的seq来序列化同一组的所有消息,以确保所有组成员看到消息的时序相同。这时候使用本地时钟生成seq就可以了,是不是很聪明呢?综上所述,要“有序”,就必须有一把尺子来衡量“有序”,可以是客户端的尺子,也可以是服务器端的尺子;大多数企业可以接受大规模的趋势和有序的、小规模的错误;绝对有序的业务,可以使用服务器的绝对定时能力;单点序列化是保证多机统一定时的常用方法。典型场景包括db主从一致性,gfs多文件一致性;一对一聊天,只需要保证发送的时间和接收的时间一致,就可以使用客户端seq;群聊,只需要保证所有接收者消息的时序一致,就需要用到服务端seq,有两种方法,一种是单点绝对时序,一种是idserial思路比较重要比起结论,希望大家有所收获。
