RocketMQ 基本原理及知识点

简介

RocketMQ 是阿里旗下(后来被纳入到Apache旗下), 使用java语言开发, 支持集群高并发, 高吞吐量的开源消息队列.

角色

NameServer 保存了topic及broker的信息, 各NameServer间不通信, 功能类似于ZooKeeper
Broker 保存消息的服务, 与NameServer保持长连接
Queue 存放消息的队列, 实际存放的是消息的offset
Producer 消息生产者, 从NameServer获取broker信息, 将消息存入queue中
Consumer 消息消费者, 从NameServer获取broker信息, 从broker中拉取消息
Tag 消息的二级分类, 可将消息细分某业务下的具体场景

Producer Group 表示各生产者生产同一类消息, 不同的服务避免生成同一个topic
Consumer Group 表示消费者消费同一类消息, 默认会平均分配对应topic下的queue, 但是一个queue只能被一个Consumer消费

与kafka架构一致, RocketMQ也有分区的概念, 创建topic时, 可以指定主从分区个数, 可将各分区均匀分布到各master broker上

架构图

在这里插入图片描述

生产

生产者根据指定topic从某个NameServer中获取broker信息(master broker), 然后根据返回的broker的信息, 然后根据指定的算法, 从中选择一个queue, 封装请求信息, 通过同步/异步/单向3种方式发送消息, 底层是通过Netty框架实现的.

其中:
同步 也是依赖于异步实现的, 阻塞等待指定时间内获取结果, 然后返回结果并将结果

SendResult result = rocketMQTemplate.syncSend(topic, message);

异步 使用了线程池发送消息, 添加Listener监听异步结果, 通过Netty异步传输消息到broker, 同步直接返回null, 等待发送完成后会异步调用Listener的成功或失败的方法.

rocketMQTemplate.asyncSend(topic, message, new SendCallback() {
    public void onSuccess(SendResult sendResult) {
    	...
    }
    public void onException(Throwable e) {
    	...
    }
}, 1000);

单向 不等待结果, 也不需要通知, 一般用于不重要的消息, 不关心是否能成功. 底层和同步逻辑一致, 不过直接返回null

同步/异步 发送之前会为请求生成一个id并存放到responseTable中, 会有线程扫描此Map, 如果超时, 则删除并通过线程池通知结果.

存储

在这里插入图片描述
当broker接收到消息后, 会先存储到commitLog中, 单个broker的各topic消息的commitLog会记录在一起, 顺序写入, 每1G生成一个新的文件, 存放在store目录下, 然后将消息在commitLog中的偏移量, tag等信息存入到queue中, 不将消息写入到queue中, 也是为了提高queue存储消息的数量, 也不会降低消息的读取速度

消费

消费者启动后会与NameServer建立长连接, 并保持心跳, 更新broker信息, 一般建议从slave broker中去消费, 这样减少master broker的压力, 增加吞吐量. 服务端会根据消费者所在组生成一个offset并存储起来, 用于当消费者再次拉取消息时从该偏移量向后获取消息, 避免消息重复消费. 消费者也可以重新指定offset.

不同的消费者组可以同时消费同一个topic下的queue消息, 但是服务端会根据消费者组生成不同的offset. 比如订单创建成功, 需要更新积分系统和发送短信, 可以生产一条订单消息, 积分系统和短信系统两个消费者组分别消费同一个消息.

消费者获取消息的方式有两种, pull 和 push.

push 虽然能保证不延迟, 但是如果生产者和消费者不匹配, 或者消费者消费能力偏弱, 容易造成消费端压力过大而崩溃. 一般生产上不建议使用此模式

pull 消息生成后会存放到broker中, 消费端可以根据自定义线程数量, 进行消息拉取, 如果生产者大批量生产消息, 则消费消息会出现延迟, 但是这样不会导致消费者因为压力过大而崩溃宕机, 相对于push模式, 更加可靠, 所以生产环境中消费者都是通过pull模式获取消息的

同一个topic下, 相同消费者组内消费者数量应小于等于topic下queue的数量, 因为分配的原则是一个queue在同一个topic下只能有一个consumer来消费, 所以如果有5个consumer来消费4个queue, 则会有一个consumer空闲.

一个消费者可以消费同一个topic下的多个queue
不同消费者组不能消费同一个topic, 会分配到queue, 但是不会消费消息, 无形中造成消息丢失的情况

顺序消息

要保证消息的顺序性, 就要将消息通过特定算法, 将消息顺序存储到同一个queue中, 并且一个消费者组中只能有一个消费者消费. 比如 秒杀系统, 各用户根据商品信息hash(id), 按照顺序进入到同一个queue中, 消费者就会根据顺序逐个进行创建订单, 保证了活动的公平性.

参考文章: SpringBoot下RocketMQ顺序消息的生产与消费

延迟消息

延迟消息通常用于指定时间后产生某事件, 如 订单15min不支付直接取消, RocketMQ延迟消息不支持随意设置时间, 在broker.conf文件中添加默认延迟消息的级别

messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
SendResult result = rocketMQTemplate.syncSend(topic, message, 1000, 4); // 表示延迟30s

RocketMQ延迟消息的原理是先将消息存放到特定的topic(SCHEDULE_TOPIC_XXXX)队列下, 每个级别都会有一个队列, 当然也会存放在commitLog中, 会有job扫描这些个队列, 当达到预期事件后, 会将消息重新投放到目的topic的queue中, 就可以完成消费了.

事务消息

事务消息类似于延迟消息, 将消息先创建到指定topic(RMQ_SYS_TRANS_HALF_TOPIC)中, 带本地事务提交, 或者查询成功后, 将消息投放到目的topic中, 即可消费

参考文章: SpringCloud整合RocketMQ实现多事务消息发送

消息不丢失

消息的丢失原因有多种, 要保证消息不被丢失, 就要从多方面考虑

生产者丢失消息 监听消息发送结果, 如果失败, 则尝试重试发送, 或做好异常记录. RocketMQ也支持设置重试次数,

broker丢失消息 设置为同步刷盘机制, 如果为异步有可能因为没来得及刷盘宕机导致数据丢失.

消费者丢失消息 由于消费者没有消费成功, 直接或者间接更改了offset会导致消息不会被消费, 所以应避免这种情况, 但是也应该考虑到消息被重复消费的情况, 最好从业务上做到消息的幂等性.

消息堆积

消息产生堆积, 一般原因都是消费者消费能力下降或服务异常, 如果还能继续存储新的消息, 则应该优先处理消费者异常, 恢复正常后扩大消费者或broker规模.

扩大broker规模: 创建新的topic, 使用一组消费者将原堆积的消息直接转存到新的topic队列中, 然后消费新的topic消息, 待消息存量恢复正常后, 终止转存, 新的topic消费完毕后, 即可停止临时服务

如何支持高并发?

首先, RocketMQ中CommitLog是顺序写入的, 磁盘顺序IO效率远超随机IO

使用了零拷贝技术, 不需要将数据在内核与用户空间内相互复制, 减少了数据副本产生, 节省了资源也提升了效率

使用MemoryMap技术, 将commitLog映射到内存, 提升了读取速度

支持异步刷盘, 将消息先写入到pageCache中, 由操作系统或者用户线程异步发起flush到disk.

集群支持读写分离, 水平扩展, 降低主broker压力, 提升了吞吐量.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值