持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
RocketMQ
概念
分布式消息队列,使用java开发。特点是高性能、低延时和高可靠。通过配置可保证消息不丢失,类似kafka,是分布式消息队列,能持久化保存数据到磁盘中。
特点
- 发布/订阅消息传递模型
- 财务级交易消息
- 各种跨语言客户端,例如Java,C / C ++,Python,Go
- 可插拔的传输协议,例如TCP,SSL,AIO
- 内置的消息跟踪功能,还支持开放式跟踪
- 多功能的大数据和流生态系统集成
- 按时间或偏移量追溯消息
- 可靠的FIFO和严格的有序消息传递在同一队列中
- 高效的推拉消费模型
- 单个队列中的百万级消息累积容量
- 多种消息传递协议,例如JMS和OpenMessaging
- 灵活的分布式横向扩展部署架构
- 快如闪电的批量消息交换系统
- 各种消息过滤器机制,例如SQL和Tag
- 用于隔离测试和云隔离群集的Docker映像
- 功能丰富的管理仪表板,用于配置,指标和监视
- 认证与授权
关键组件介绍
- Broker: 一台实例机器,集群中分为
master和slave。 - Producer: 生产者,用来发送消息到
Master Broker上面,可指定发送的topic和tag。 - Consumer:消费者,消费
Broker上面消息,master和slave都可被消费。 - NameSrv:注册中心,存放
Broker的元数据。 - Topic:消息队列组,分为多个
queue。 - queue:队列,用来传输消息。
- tag:标识消息队列的标签,区分
Topic中不同类型消息。
安装
下载、配置
- 下载rocketmq,下载地址:www.apache.org/dyn/closer.…
- 解压文件到本地目录
- 配置
ROCKETMQ_HOME,指定地址到bin目录,例如D:\code\rocketmq-all-4.6.0-bin-release\bin
测试
进入bin目录,打开cmd,分别执行以下步骤
- 启动namesrv ,执行
start mqnamesrv.cmd - 启动broker,执行
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true - 启动示例Consumer,等待消费消息,分别执行:
set NAMESRV_ADDR=localhost:9876
tools.cmd org.apache.rocketmq.example.quickstart.Consumer
- 切换一个cmd,启动示例Producer,发送消息,分别执行:
set NAMESRV_ADDR=localhost:9876
tools.cmd org.apache.rocketmq.example.quickstart.Producer
启动Producer后Consumer控制台会打印出日志
ConsumeMessageThread_18 Receive New Messages: [MessageExt [queueId=1, storeSize=227, queueOffset=249, sysFlag=0, bornTimestamp=1652792240927, bornHost=/10.42.46.101:59343, storeTimestamp=1652792240929, storeHost=/10.42.46.101:10911, msgId=0A2A2E6500002A9F0000000000037567, commitLogOffset=226663, bodyCRC=638172955, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=250, CONSUME_START_TIME=1652792253875, UNIQ_KEY=0000000000000000000000000000000100003764951D56E4E31F03E7, CLUSTER=DefaultCluster, WAIT=true, TAGS=TagA}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81, 32, 57, 57, 57], transactionId='null'}]]
ConsumeMessageThread_11 Receive New Messages: [MessageExt [queueId=1, storeSize=227, queueOffset=248, sysFlag=0, bornTimestamp=1652792240903, bornHost=/10.42.46.101:59343, storeTimestamp=1652792240909, storeHost=/10.42.46.101:10911, msgId=0A2A2E6500002A9F00000000000371DB, commitLogOffset=225755, bodyCRC=801108784, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=250, CONSUME_START_TIME=1652792253875, UNIQ_KEY=0000000000000000000000000000000100003764951D56E4E30703E3, CLUSTER=DefaultCluster, WAIT=true, TAGS=TagA}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81, 32, 57, 57, 53], transactionId='null'}]]
可以看到消息发送和接收日志,说明通信正常
基本原理
Producer发消息时会轮询指定Topic下的所有队列进行负载均衡。Producer 会定时向 Broker 心跳,证明其存活。而 Broker 会定时检测,判断是否有 Producer 异常下线。初始化任务:
- 如果没有指定
namesrv地址,将会自动寻址 - 启动定时任务:更新
namesrv地址、从namesrv更新Topic路由信息、清理已经挂掉的Broker、向所有Broker发送心跳 - 启动负载均衡的服务 发送失败会执行自动重试,自动重试的原则如下:
- 至多重试2次。
- 如果同步模式发送失败,则轮转到下一个
Broker,如果异步模式发送失败,则只会在当前Broker进行重试。这个方法的总耗时时间不超过sendMsgTimeout设置的值,默认10s。 - 如果本身向
Broker发送消息产生超时异常,就不会再重试。
Consumer
Consumer有消费者组的概念,消费者组内的Consumer会负载均衡消费消息,当相同消费者分组中有新的 Consumer 上线,或者老的 Consumer 下线,会重新分配 Topic 的 Queue 到目前消费分组的 Consumer 们。Consumer 会定时向 Broker 心跳,证明其存活。而 Broker 会定时检测,判断是否有 Consumer 异常下线。RocketMQ 支持两种消息模式:集群消费(Clustering)和广播消费(Broadcasting)。
在集群消费下,同一条消息只会被相同消费者分组的一个消费者所消费,默认是集群消费。 在广播消费下,同一条消息会被相同消费者分组的所有消费者所消费。
消息存储
RocketMQ的消息存储是由consume queue和commit log配合完成的。
consume queue是消息的逻辑队列,相当于字典的目录,用来指定消息在物理文件commit log上的位置。我们可以在配置中指定consumequeue与commitlog存储的目录,每个topic下的每个queue都有一个对应的consumequeue文件
CommitLog:消息存放的物理文件,每台broker上的commitlog被本机所有的queue共享,不做任何区分。 文件的默认位置如下,仍然可通过配置文件修改
消息订阅
RocketMQ消息订阅有两种模式,一种是Push模式,即MQServer主动向消费端推送;另外一种是Pull模式,即消费端在需要时,主动到MQServer拉取。但在具体实现时,Push和Pull模式都是采用消费端主动拉取的方式。
消费端会通过RebalanceService线程,10秒钟做一次基于topic下的所有队列负载:
- 遍历
Consumer下的所有topic,然后根据topic订阅所有的消息 - 获取同一
topic和Consumer Group下的所有Consumer - 然后根据具体的分配策略来分配消费队列,分配的策略包含:平均分配、消费端配置等
消费端每隔一段时间主动向broker发送拉消息请求,broker在收到Pull请求后,如果有消息就立即返回数据,消费端收到返回的消息后,再回调消费者设置的Listener方法。如果broker在收到Pull请求时,消息队列里没有数据,broker端会阻塞请求直到有数据传递或超时才返回。
顺序消息
消息有序指的是可以按照消息的发送顺序来消费。RocketMQ通过轮询所有队列的方式来确定消息被发送到哪一个队列。在获取到路由信息以后,会根据MessageQueueSelector实现的算法来选择一个队列。
消息重复
RocketMQ不保证消息不重复,需要在业务端去重。
事务消息
RocketMQ第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息的状态。
实际配置
Producer实践
- 一个应用尽可能用一个
Topic,消息子类型用tags来标识,tags可以由应用自由设置。只有发送消息设置了tags,消费方在订阅消息时,才可以利用tags在broker做消息过滤。 - 每个消息在业务层面的唯一标识码,要设置到
keys字段,方便将来定位消息丢失问题。由于是哈希索引,请务必保证key尽可能唯一,这样可以避免潜在的哈希冲突。 - 消息发送成功或者失败,要打印消息日志,务必要打印
sendresult和key字段。 - 对于消息不可丢失应用,务必要有消息重发机制。例如:消息发送失败,存储到数据库,能有定时程序尝试重发或者人工触发重发。
- 某些应用如果不关注消息是否发送成功,请直接使用
sendOneWay方法发送消息。
Consumer实践
- 消费过程要做到幂等(即消费端去重)
- 尽量使用批量方式消费方式,可以很大程度上提高消费吞吐量。
- 优化每条消息消费过程