本文是ROCKETMQ的第一篇,主要将介绍ROCKETMQ核心概念
一、架构组网
1.1 RocketMQ 部署架构
- NameServer : Topic 的路由注册中心,为客户端根据 Topic 提供路由服务,从而引导客户端向 Broker 发送消息。NameServer 之间的节点不通信。路由信息在 NameServer 集群中数据一致性采取的最终一致性。
- Broker: 消息存储服务器,分为两种角色:Master 与 Slave。主服务承担读写操作,从服务器作为一个备份,当主服务器存在压力时,从服务器可以承担读服务(消息消费)。所有 Broker,包含 Slave 服务器每隔 30s 会向 NameServer 发送心跳包,心跳包中会包含存在在 Broker 上所有的 Topic 的路由信息。
- Client:包括 Producer(消息发送者)和 Consumer(消费消费者)。客户端在同一时间只会连接一台 NameServer,只有在连接出现异常时才会向尝试连接另外一台。客户端每隔 30s 向 NameServer 发起 Topic 的路由信息查询。
1.2 消息订阅模型
发布与订阅模式
- Topic:一类消息的集合,消息发送者将一类消息发送到一个主题中。
- ConsumerGroup:消息消费组,一个消费单位的群体,消费组首先在启动时需要订阅需要消费的 Topic。
一个 Topic 可以被多个消费组订阅,同样一个消费组也可以订阅多个主题。一个消费组拥有多个消费者。
二、核心概念
2.1 消费模式
- 广播模式:一个消费组内的所有消费者每一个都会处理 Topic 中的每一条消息,通常用于刷新内存缓存。
- 消费进度:存储在用户的主目录,默认文件全路径名:
${USER_HOME}/.rocketmq_offsets
- 消费进度:存储在用户的主目录,默认文件全路径名:
- 集群模式:一个消费组内的所有消费者共同消费一个 Topic 中的消息,即分工协作,一个消费者消费一部分数据,启动负载均衡。
- 消费进度:消息消费进度存储在 Broker 端,
${ROCKETMQ_HOME}/store/config/consumerOffset.json
- 消费进度:消息消费进度存储在 Broker 端,
2.2 消费队列负载算法和重平衡机制
集群模式下,消费者分配消息使用的负载均衡算法:
- AllocateMessageQueueAveragely:平均分配
- AllocateMessageQueueAveragelyByCircle:轮流平均分配
消费队列重平衡机制: 在 RocketMQ 客户端中会每隔 20s 去查询当前 Topic 的所有队列、消费者的个数,运用队列负载算法进行重新分配,然后与上一次的分配结果进行对比,如果发生了变化,则进行队列重新分配;如果没有发生变化,则忽略。
2.3 消费模型
RocketMQ 提供了并发消费、顺序消费两种消费模型。
- 并发消费:对一个队列中消息,每一个消费者内部都会创建一个线程池,对队列中的消息多线程处理,即偏移量大的消息比偏移量小的消息有可能先消费。
- 顺序消费:在某一项场景,例如 MySQL binlog 场景,需要消息按顺序进行消费。在 RocketMQ 中提供了基于队列的顺序消费模型,即尽管一个消费组中的消费者会创建一个多线程,但针对同一个 Queue,会加锁。
温馨提示:并发消费模型中,消息消费失败默认会重试 16 次,每一次的间隔时间不一样;而顺序消费,如果一条消息消费失败,则会一直消费,直到消费成功。故在顺序消费的使用过程中,应用程序需要区分系统异常、业务异常,如果是不符合业务规则导致的异常,则重试多少次都无法消费成功,这个时候一定要告警机制,及时进行人为干预,否则消费会积压。
2.4 延迟消息
开源版本的 RocketMQ 目前并不支持任意精度的定时消息。所谓的定时消息就是将消息发送到 Broker,但消费端不会立即消费,而是要到指定延迟时间后才能被消费端消费。
RocketMQ 目前支持指定级别的延迟,其延迟级别如下:
1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
具体实现原理参考这个沸点:ROCKETMQ延迟队列
2.5 事务消息
事务消息并不是为了解决分布式事务,而是提供消息发送与业务落库的一致性,其实现原理就是一次分布式事务的。 实现原理如下:
实现原理有点类似于两阶段提交+定时轮循
应用程序在事务内完成相关业务数据落库后,需要同步调用 RocketMQ 消息发送接口,发送状态为 prepare 的消息,消息发送成功后 RocketMQ 服务器会回调 RocketMQ 消息发送者的事件监听程序,记录消息的本地事务状态,该相关标记与本地业务操作同属一个事务,确保消息发送与本地事务的原子性。
RocketMQ 在收到类型为 prepare 的消息时,会首先备份消息的原主题与原消息消费队列,然后将消息存储在主题为 RMQ_SYS_TRANS_HALF_TOPIC 的消息消费队列中,就是因为这样,消费端并不会立即被消费到。
RocketMQ 消息服务器开启一个定时任务,消费 RMQ_SYS_TRANS_HALF_TOPIC 的消息,向消息发送端(应用程序)发起消息事务状态回查,应用程序根据保存的事务状态回馈消息服务器事务的状态(提交、回滚、未知),如果是提交或回滚,则消息服务器提交或回滚消息,如果是未知,待下一次回查,RocketMQ 允许设置一条消息的回查间隔与回查次数,如果在超过回查次数后未知消息的事务状态,则默认回滚消息。
没有事务消息,也可以使用其他方式替代:
- 严格的事务一致性:本地事务表方式,发送前记录事务表,发送后删除,定时任务定时轮询发送
- 非严格,基于补偿的思路,发送失败采用重试机制,发送失败记录操作,人工处理