MQ系列12:如何保证消息顺序性
MQ系列1:消息中间件执行原理
MQ系列2:消息中间件的技术选型
MQ系列3:RocketMQ 架构分析
MQ系列4:NameServer 原理解析
MQ系列5:RocketMQ消息的发送模式
MQ系列6:消息的消费
MQ系列7:消息通信,追求极致性能
MQ系列8:数据存储,消息队列的高可用保障
MQ系列9:高可用架构分析
MQ系列10:如何保证消息幂等性消费
MQ系列11:如何保证消息可靠性传输
1 介绍
消息的有序性在很多业务场景中占有很重要的位置。
比如购物场景,需要按照 创建订单 --> 订单付款 --> 完成订单 顺序执行。
又比如出行场景,接单 --> 接送到达目的地 --> 付款 --> 完成订单。
这种是严格按照顺序执行的,这样的顺序消费才不会出问题,而且各个订单之间是互相独立和并行执行的。
所以,在MQ中,如何稳定地保证顺序性消息处理,是一个不可避免的话题。
2 消息的有序性说明
消息的有序执行,一般不是单个组件的能力。而是整个消息从生产,排队,存储到消费都是有序的,比如上面提到的购物和出行场景。
这就要求我们在消息队列(如果是Kafka,还是RocketMQ、RabbitMQ)中,保证以下前提:
- 消息生产的有序性:即生产者组件有序发送消息
- 消息入出队列的有序性:即消息是按照进入的先后顺序排队列放的,遵循FIFO原则。
- 消息的存储的有序性:与上一点一致,部分场景下为了提高可用,就是要持久化到磁盘,这时候应该遵循有序存放,才能保证后续有序消费
- 消息消费的有序性:即按照顺序进行消费。又分为全局顺序消息与部分顺序消息,全局是指Topic下的所有消息都要保证顺序;部分顺序消息保证每一组消息被顺序消费即可。
这边还有个问题,如果想让全局都是顺序性消费,那么只能用一个消费者去消费队列(一般来说也是单个生产者),这是会严重影响整体性能的,一般没这个,都是分组顺序执行消费的。
2.1 消息生产的有序性
要保证整个消息队列的有序性执行,首先要保证消息生产的有序性。
RocketMQ在Broker中防止了很多Topic,主题(Topic)可以看做消息的归类,我们将消息进行类型划分,相同类型的消息称为一个 Topic。比如我们在淘宝或京东上购买商品的的过程,就可能产生:购物车消息、交易消息、物流消息等,1条消息必然归属于1个 Topic 。
1个 Topic可以有0 ~ n 个生产者向其发送消息;也可以被 0~n 个消费者订阅和处理,于是就有出现了生产者组和消费者组,如下图:
或者同一个Topic中,创建不同的Queue,同一个消息生产者将消息隔离发送到不同的Queue中:
按照上述的模式,同理,我们只需要保证一组相同的消息按照给定的顺序存入同一个队列中,就能保证生产者有序存储,比如一次完整的消费过程:创建订单、付款、完成订单按照顺序在一个队列(Queue)中执行那就可以了。
★ 同时我们要保证同一组的消息在消息生产的时候投送到一个组中。这个相对来说不难,可以这么做:
- 比如一个订单的多个子消息的父订单号是一致,我们把这些消息按照订单号取模,投送到对应的Queue中就行了,比如 订单号 % 队列数量( 163105015 % 9)
- 发送消息自定义消息标签(消息标签可以用队列编号命名),一组消息使用同一个标签,改组标签对应的消息都投向标签所在的队列。
★ 业务程序方面,必须使用同步发送的方式,这样才能保证生产者发送的消息有序,否则按照FIFO的原则,很可能 订单完成 会被先消费。
但是我们业务程序,比如Java代码中为了提升性能,可能使用多线程的模式进行事件触发。多线程下保证生产者顺序性,可以使用锁并配合 spring的publish event(按照顺序执行的内部队列),持久化之后,再按照先进先出的顺序推送消息进入MQ中。
可以参考下 ,大概就是将你的事件进行顺序化一下。
★ 上述方法也不能完完全全的避免顺序化执行。如果broker服务发生故障,或者消息发生丢失,都有可能导致事件消费不完整,出现不一致的问题。
2.2 消息有序性存储
Broker 存储架构采用文件存储机制(类似Kafka),即直接在磁盘上使用文件来保存消息,而不是采用Redis或者MySQL之类的持久化工具。
它会把消息存储所属相关的文件存储在ROCKETMQ_HOME下,包含三个部分:
- CommitLog 消息元数据
- ConsumeQueue 消息逻辑队列
- IndexFile 索引文件
存储消息的元数据,所有消息都会顺序存入到CommitLog文件中。
ConsumeQueue是指存储消息在CommitLog上的索引,一个MessageQueue一个文件,记录当前MessageQueue被哪些消费者组消费到了哪一条CommitLog。
所以一切都是顺序性操作下来的,而且按照 MessageQueue 做了隔离了,不用担心乱序的问题。详细参考 《MQ系列8:数据存储,消息队列的高可用保障
》
2.3 消息消费的有序性
最后一步就是消费的有序性了,既然消息生产和消息持久化都可以做到有序性。那么只要保证消费的有序性,就能保证整个消息队列的有序执行。
这边以RocketMQ为例子,RockerMQ采用MessageListener 回调函数进行监听,监听到消息之后进行数据处理。MessageListener主要提供了两种消费模式,如下:
- 有序消费模式MessageListenerOrderly
- 并发消费模式MessageListenerConcurrently
其中有序消费模式有序消费模式MessageListenerOrderly可以保证按照顺序进行消息处理。但是消费的业务代码实现是多线程并行的,依然是无法保证的。
实际上RocketMQ也是这么做的,MessageListenerConcurrently拉到消息之后会提交到线程池去消费,而MessageListenerOrderly则是通过分布式锁和本地锁保证同时只有一条线程去消费一个队列(Queue)上的数据。
这种消费模式就是使用以下3把锁来确保顺序性:
- broker端的分布式锁
- messageQueue的本地synchronized锁
- ProcessQueue的本地consumeLock
3 总结
要消息的顺序性消费:需要保持先后顺序的消息放到同一个消息队列中(kafka中就是partition,rabbitMq中就是queue),然后使用线程池消费的时候使用分布式锁和本地锁保证同时只有一条线程去消费一个队列(Queue)上的数据。
MQ系列12:如何保证消息顺序性的更多相关文章
- 转:TCP为什么要3次握手和4次挥手时等待2MSL、 TCP如何保证消息顺序以及可靠性到达
关于tcp三次握手.四次挥手可以看这里:TCP与UDP的差别以及TCP三次握手.四次挥手 1.TCP为甚要3次握手? 在谢希仁著<计算机网络>第四版中讲“三次握手”的目的是“为了防止已失效 ...
- MQ系列5:RocketMQ消息的发送模式
MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 在之前的篇章中,我们学习了RocketMQ的原理, ...
- 分布式场景下Kafka消息顺序性的思考
如果业务中,对于kafka发送消息异步消费的场景,在业务上需要实现在消费时实现顺序消费, 利用kafka在partition内消息有序的特点,消息消费时的有序性. 1.在发送消息时,通过指定parti ...
- Pulsar の 保证消息的顺序性、幂等性和可靠性
原文链接:Pulsar の 保证消息的顺序性.幂等性和可靠性 一.背景 前面两篇文章,已经介绍了关于Pulsar消费者的详细使用和自研的Pulsar组件. 接下来,将简单分析如何保证消息的顺序性.幂等 ...
- RabbitMQ保证消息的顺序性
当我们的系统中引入了MQ之后,不得不考虑的一个问题是如何保证消息的顺序性,这是一个至关重要的事情,如果顺序错乱了,就会导致数据的不一致. 比如:业务场景是这样的:我们需要根据mysql的b ...
- MQ系列11:如何保证消息可靠性传输(除夕奉上)
MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...
- MQ系列10:如何保证消息幂等性消费
MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...
- MQ系列6:消息的消费
MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 在之前 ...
- MQ系列8:数据存储,消息队列的高可用保障
MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...
- LinkedHashMap如何保证顺序性
一. 前言 先看一个例子,我们想在页面展示一周内的消费变化情况,用echarts面积图进行展示.如下: 我们在后台将数据构造完成 HashMap<String, Integer> map ...
随机推荐
- Activiti 7 启动流程实例
首先,考虑下面这样一个流程图 主要的流程定义如下: <process id="demo" name="demo" isExecutable="t ...
- 为什么说 ICMP 协议是网络最强辅助
大家好,我是风筝 轻解网络系列又来了.已有高清 PDF 版本可以离线阅读了,全册 65 页,如果有需要离线版的高清 PDF 可以直接下载. 今天咱们说说 ICMP 协议.ICMP 可谓是网络世界中的最 ...
- 《花雕学AI》13:早出对策,积极应对ChatGPT带来的一系列风险和挑战
ChatGPT是一款能和人类聊天的机器人,它可以学习和理解人类语言,也可以帮人们做一些工作,比如翻译.写文章.写代码等.ChatGPT很强大,让很多人感兴趣,也让很多人担心. 使用ChatGPT有一些 ...
- day9:文件相关操作&文件扩展模式&文件相关函数
文件的操作 # 文件的写入 # 1.打开文件 fp = open("ceshi1.txt",mode="w",encoding="utf-8" ...
- TS(二)内置对象与class类
内置对象 ECMA内置对象 Boolean.Number.String.RegExp.Date.Error const regexp:RegExp = /\w\d\s const number:Num ...
- DRF版本控制(源码分析)
DRF中版本控制的五种情况(源码分析) 在restful规范中要去,后端的API中需要体现版本. drf框架中支持5种版本的设置. 1. URL的GET参数传递(*) 示例: user/?versio ...
- 景顺长城基于 Apache APISIX 在金融云原生的生产实践
本文介绍了景顺长城在金融云原生架构演进中选择 APISIX 作为网关工具的技术细节,同时分享了使用 APISIX 的实践细节,并对 APISIX 的未来展望进行了探讨. 作者李奕浩,景顺长城信息技术部 ...
- Junit启动测试mybatis xml文件BindingException: Invalid bound statement问题
背景:1.正常启动,xml文件放在java目录和resource目录下均正常 2.junit启动,xml文件放在resource目录下正常,放在java目录下报BindingException错误 m ...
- 14-压缩css
const { resolve } = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const M ...
- 统计机器学习-Introduction to Statistical Learning-阅读笔记-CH4-Classification
response variable: quantitative qualitative / categorical methods for classification first predict t ...