重新认识 RocketMQ (1)

181 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

RocketMQ

概念

分布式消息队列,使用java开发。特点是高性能、低延时和高可靠。通过配置可保证消息不丢失,类似kafka,是分布式消息队列,能持久化保存数据到磁盘中。

特点

  • 发布/订阅消息传递模型
  • 财务级交易消息
  • 各种跨语言客户端,例如Java,C / C ++,Python,Go
  • 可插拔的传输协议,例如TCP,SSL,AIO
  • 内置的消息跟踪功能,还支持开放式跟踪
  • 多功能的大数据和流生态系统集成
  • 按时间或偏移量追溯消息
  • 可靠的FIFO和严格的有序消息传递在同一队列中
  • 高效的推拉消费模型
  • 单个队列中的百万级消息累积容量
  • 多种消息传递协议,例如JMS和OpenMessaging
  • 灵活的分布式横向扩展部署架构
  • 快如闪电的批量消息交换系统
  • 各种消息过滤器机制,例如SQL和Tag
  • 用于隔离测试和云隔离群集的Docker映像
  • 功能丰富的管理仪表板,用于配置,指标和监视
  • 认证与授权

关键组件介绍

  • Broker: 一台实例机器,集群中分为masterslave
  • Producer: 生产者,用来发送消息到Master Broker上面,可指定发送的topictag
  • Consumer:消费者,消费Broker上面消息,masterslave都可被消费。
  • 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 queuecommit log配合完成的。 consume queue是消息的逻辑队列,相当于字典的目录,用来指定消息在物理文件commit log上的位置。我们可以在配置中指定consumequeuecommitlog存储的目录,每个topic下的每个queue都有一个对应的consumequeue文件

CommitLog:消息存放的物理文件,每台broker上的commitlog被本机所有的queue共享,不做任何区分。 文件的默认位置如下,仍然可通过配置文件修改

消息订阅

RocketMQ消息订阅有两种模式,一种是Push模式,即MQServer主动向消费端推送;另外一种是Pull模式,即消费端在需要时,主动到MQServer拉取。但在具体实现时,Push和Pull模式都是采用消费端主动拉取的方式。 消费端会通过RebalanceService线程,10秒钟做一次基于topic下的所有队列负载:

  • 遍历Consumer下的所有topic,然后根据topic订阅所有的消息
  • 获取同一topicConsumer Group下的所有Consumer
  • 然后根据具体的分配策略来分配消费队列,分配的策略包含:平均分配、消费端配置等

消费端每隔一段时间主动向broker发送拉消息请求,broker在收到Pull请求后,如果有消息就立即返回数据,消费端收到返回的消息后,再回调消费者设置的Listener方法。如果broker在收到Pull请求时,消息队列里没有数据,broker端会阻塞请求直到有数据传递或超时才返回。

顺序消息

消息有序指的是可以按照消息的发送顺序来消费。RocketMQ通过轮询所有队列的方式来确定消息被发送到哪一个队列。在获取到路由信息以后,会根据MessageQueueSelector实现的算法来选择一个队列。

消息重复

RocketMQ不保证消息不重复,需要在业务端去重。

事务消息

RocketMQ第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息的状态。

实际配置

Producer实践

  • 一个应用尽可能用一个 Topic,消息子类型用 tags 来标识,tags 可以由应用自由设置。只有发送消息设置了tags,消费方在订阅消息时,才可以利用 tagsbroker 做消息过滤。
  • 每个消息在业务层面的唯一标识码,要设置到 keys 字段,方便将来定位消息丢失问题。由于是哈希索引,请务必保证 key 尽可能唯一,这样可以避免潜在的哈希冲突。
  • 消息发送成功或者失败,要打印消息日志,务必要打印 sendresultkey 字段。
  • 对于消息不可丢失应用,务必要有消息重发机制。例如:消息发送失败,存储到数据库,能有定时程序尝试重发或者人工触发重发。
  • 某些应用如果不关注消息是否发送成功,请直接使用sendOneWay方法发送消息。

Consumer实践

  • 消费过程要做到幂等(即消费端去重)
  • 尽量使用批量方式消费方式,可以很大程度上提高消费吞吐量。
  • 优化每条消息消费过程