RocketMQ常见问题总结(非常好的总结性的文章)
初识RocketMQ | RocketMQ(RocketMQ官网!!)
万字长文讲透 RocketMQ 的消费逻辑(这篇文章很全面,全面介绍了rocketMQ)
千锋教育RocketMQ全套视频教程,快速掌握MQ消息中间件_哔哩哔哩_bilibili
📎rocketmq.pdf(千峰教育配套课件)
RocketMQ为什么这么快?
RocketMQ为什么这么快?我从源码中扒出了10大原因! - 三友的java日记 - 博客园(这篇文章信息量很大,值得阅读)
- 采用了Netty网络模型。
- 底层采用了零拷贝机制,从Socket接受消息后不需要经过用户缓冲区,而是在内核缓冲区传递。
- 消息写到CommitLog磁盘是追加写顺序写,远快于随机写。(MySQL中的redo log同理)
- 有异步刷盘机制。(redis中的aof同理)
- 支持批量发送消息。以List集合形式。
延迟消息
5 张图带你理解 RocketMQ 延时消息机制(4.x的延迟消息)
弥补延时消息的不足,RocketMQ 基于时间轮算法实现了定时消息!(4.x和5.x延迟消息实现 比较)
讲讲RocketMQ的延迟消息?
说4.x的实现,然后说4.x的缺点,然后引出5.x的实现
4.x
生产者生产的时候指定延迟级别(总共有18个延迟级别,broker也有18个延迟队列),broker会根据延迟级别将消息放到指定的延迟队列。
定时任务线程池会有 18 个线程来对相应的18个延时队列进行轮询/遍历调度,每个线程调度一个延时级别,调度任务把延时消息再投递到原始队列,这样 Consumer 就可以拉取到了。
问题:
- 延迟级别只有18个,不能随意定制时间。
- 延迟时间不准, 定时任务线程可能因为需要处理(遍历/轮询)大量消息导致延迟误差较大。
5.x
(我的理解,4.x是遍历任务队列,而5.0是遍历时间,将任务挂在时间上)
因此5.0采用了时间轮解决上述问题。时间轮不用遍历每个任务,而是每一个时间节点上的任务用链表串起来,当时间轮上的指针移动到当前的时间时,这个时间节点上的全部任务都执行。
时间轮只有60s,如何记录130s的任务?
用一个round记录轮数,例如130s的round=2,然后挂在时间轮的第10个节点。
延迟使用场景?
- 关闭xxx分钟内未支付的订单。
- 消息重试机制 使用延迟队列。
和重试机制有什么关系
重试本质就是延迟消息,某topic需要重试的消息会被加入到延迟消息队列中。
在4.x实现中,定时任务扫描延迟队列时,定时任务是怎么知道某条消息 该投递了(或者说时间到了)
Broker 端接收到生产者的写入消息请求后,首先都会将消息写到 commitlog 中,然后异步写到consumequeue中。如果是延迟消息,RocketMQ 将 Topic 的名称修改为SCHEDULE_TOPIC_XXXX,并根据延迟级别确定要投递到哪个队列下。同时,还会将消息原来要发送到的目标 Topic 和队列信息存储到消息的属性中。
关键,在写入延迟队列的consumeQueue中的时候,会计算这个消息应该被投递的时间,并存储在tag的hashCode中(我们知道consumeQueue中的每个逻辑消息由三个部分组成)。后期定时任务扫的时候,就根据tag的hashcode这个时间判断是否重新投递。
集群相关/可用性,Dleger集群
有没有担心过 Rocketmq 里边的这个信息?假如说忽然这个实例宕了的话怎么办?就是你的这些没有来得及消费的这些消息怎么处理呢?
从可用性入手,这个不是问可靠性。
- 采用集群模式保证高可用,broker的主从模式,主节点挂了走从节点消费。
- 主从模式下,采用同步复制的机制,保证主-从消息的强一致性。
- 采用Delger集群模式。
如果RocketMQ集群都挂了怎么办?
- 异地多活
- 降级处理。采用一个断路器监听,挂了降级走redis或者其他中间件。
broker常见的主从集群模式?
38-RocketMQ核心概念-Dledger高可用集群的选举逻辑_哔哩哔哩_bilibili
注意,读写数据都在leader上,这和kafka的逻辑相同,follower只做备份同步。
- 多主多从 的同步方式。
- 多主多从 的异步方式。
- 多主无从。
- Dleger高可用集群(只有Dleger支持自动主从切换)。
Dleger集群模式?(自动主从切换,且防止脑裂)
38-RocketMQ核心概念-Dledger高可用集群的选举逻辑_哔哩哔哩_bilibili
默认是一主两从,支持自动主从切换以及leader选举机制。
Leader会不断发送心跳给follwer。节点有三个状态,leader,follwer和candidate。
每个时间片走完(时间片内部又划分为选举时间和工作时间),就会重新选举,没有永久的leader。为什么这样,因为这样可以解决脑裂的问题(zk就有脑裂的问题)。
什么是脑裂:脑裂就是因为网络原因产生了分区现象,导致其他follwer以为leader挂了,其实leader还在和客户端进行交互,最终出现两个leader。
事务消息
25-消息示例-事务消息的执行逻辑_哔哩哔哩_bilibili(视频可以多看几遍)
事务消息发送 | RocketMQ(官方文档)
事务消息 也是分布式事务的一种解决方案,在RoMQ中采用了两阶段提交的方式。
事务消息 确保本地事务执行和消息发送 是原子的(就是本地事务成功,消息成功投递。若本地事务失败,消息取消投递)。
这里指的是生产者的事务消息。
事务消息 的原理
事务消息 也是分布式事务的一种解决方案,在RocketMQ中采用了两阶段提交的方式
- 第一阶段。首先给producer设置一个事务监听器
trasactionListener实现类,并且在提交事务时调用sendMessageInTransaction方法。这时producer会给broker发送一个half消息,half消息不会消费者消费。 - 第二阶段。broke收到half消息后会回复并 调用 事务监听器中的
executeLocalTransaction方法,在这个方法中可以执行本地事务(比如业务逻辑,落库相关的操作)。根据方法返回参数判断第二阶段是提交还是回滚。
-
- 若本地事务方法返回Commit枚举,则提交half消息,half消息可以被消费。
- 若本地事务方法返回Rollback枚举,则half消息被抛弃。
- 若本地事务方法返回Unknow枚举, 则half消息暂时不处理,后续回查checkLocalTransaction方法。
事务消息的应用场景
就是假如现在有个场景
需要发送消息和落库保持一致。比如我是一个中台系统,我收到了上游的消息,需要在中台落库并发送给下游,那如何保证消息落库和发送给下游MQ的一个原子性呢。就可以采用事务消息,将落库的逻辑放在trasactionListener的方法中,中台发送完half消息会调用trasactionListener方法中的落库逻辑。
事务消息的实现
half消息发送到哪?
half消息发送到rocketMQ一个专门的halfTopic(类似于延迟消息)中,根本没有发送到指定的topic,所以消费者看不到half消息,也就消费不了。
消息存储commitlog
消费者consumer提交offset给broker是 每消费一条消息就提交一个offset吗?
当然不是,这样效率多低呀,RocketMQ 消费者消费完一批数据后, 会将队列的进度保存在本地内存,但还需要将队列的消费进度持久化。
如果是拉取消息,有两个场景会同步consumer的offset
- 拉取消息服务会在拉取消息时,携带该队列的消费进度,提交给 Broker 的拉取消息处理器。
- 消费者定时任务,每隔5秒将本地缓存中的消费进度提交到 Broker 的消费者管理处理器。
每个consumeQueue都有消费点位offset,这个点位存在哪个文件(扩展)?
在config下的consumerOffset.json中(集群消费模式下)
在json文件中可以看到,(集群消费模式)每个topic为每个消费者组都维护了offset,记录了每个consumerQueue(队列)的消费进度。
在进度文件 consumerOffset.json 里,数据以 key-value 的结构存储,key 表示:主题@消费者组 , value 是 consumequeue 中每个队列对应的逻辑偏移量 。