28. 业务上需要顺序消费,怎么保证时序性

2023-07-15  本文已影响0人  木叶苍蓝

消息传输和消费的时序性,是消息队列应用中一个非常重要的问题
很多业务场景都需要考虑消息投递的时序性
例如,电商中的订单状态流转,数据库的binlog 分发

消息顺序消费有哪些困难

消息队列中的队列是一个有序的数据结构,消息传递是顺序的
在实际开发中,特别是分布式场景下,消息的有序性是很难保证的

分布式的时钟问题

消息的生产者,消费者和队列存储,可能分布在不同的机器上,不同的机器使用各自的本地时钟
由于服务器存在时钟偏斜等问题,本地时间会出现不一致
不能用消息发送和到达的时间戳作为时序判断标准
分布式系统下缺乏全局时钟,使得绝对的时间顺序实现起来更加困难

消息发送端和消费端的集群

生产者和消费者都是集群部署,通过 ProducerGroup 和 ConsumerGroup 的方式来运行
消息发送端发送时的时序不能用来作为消息发送的有序判断
消费端可能存在多个实例,即使队列内部是有序的
由于存在消息的分发过程,不同消费实例的顺序难以全局统一,也无法实现绝对的有序消费

消息重传等的影响

消息队列在传输消息时,可能会出现网络抖动导致的消息发送失败等
对这种场景的兼容,一般是通过进行合理地重传
消息的重传发生在什么时候是不可预知的,这也会导致消息传输出现乱序

网络及内部并发

如果单纯地依靠消息队列本身来保证,那么在跨实例的情况下
因为网络传输的不稳定会有先后顺序,以及内部消费的并发等,仍然无法实现绝对有序
保证消息绝对的有序,实现起来非常困难
除非在服务器内部,并且一个生产者对应一个消费者

解决消息队列的有序性有哪些手段呢?

消息传输的有序性和不同的消息队列,不同业务场景,以及技术方案的细节等都要关系
解决消息传输的有序性,需要依赖消息队列提供对应的方式

从消息队列自身的角度,可以分为全局有序和局部有序

Kafka 顺序消息

Kafka 保证消息的 Partition 内的顺序

例如,电商系统中的订单流转信息,在写入 Kafka 时通过订单 ID 进行分发
保证同一个订单 ID 的消息都会被发送到同一个 Partition 中

比较特殊的情况——消息失败重发的场景

比如同一个订单下的消息1和2,如果1发送失败了,重发的时候可能会出现在2的后边,可以通过设置”max.in.flight.requests.per.connection“ 参数来解决

RocketMQ 顺序消息

RocketMQ 保证消息在同一个 Queue 中的顺序性,也就是满足队列的先进先出原则
如果把对应一个业务主键的消息都路由到同一个 Queue 中就可以实现消息的有序传输
并且 RocketMQ 额外支持 Tag 的方式
可以对业务消息做进一步的拆分,在消费时相对更加灵活

从业务角度保证顺序消费

消息消费的有序性,是一个业务场景的设计问题。可以在业务中进行规避,或者通过合理的设计方案来解决。

消息传输的有序性是否有必要

在你的业务中是否必须实现绝对的消息有序?或者是必须要有消息队列这样的技术手段?

比如在一个订单状态消息流转的业务场景中,
订单会有创建成功,待付款,已支付,已发货的状态,订单状态的更新需要保证有序性。
如果要实现的功能是根据发货的状态,进行物流通知用户的功能
因为这个状态是单调不可逆向的,可以只关注最后是否已经发货的状态。

业务中如何实现有序消费

总结

消息的有序性可以分为时间上的有序和业务上的有序
绝对的时间有序实现起来时非常困难的
消息队列只是一个消息传输的解决方案
可以通过业务中不通的场景,进行合理的设计,实现业务上的有序性

上一篇 下一篇

猜你喜欢

热点阅读