上一次学习 RocketMQ 的相关知识已经可以追溯到十多天之前了,时间有些久远,因此今天对之前学习的 RocketMQ 的十一条八股进行复习。
RocketMQ 具有低延迟、高吞吐量、高可用性等特点,特别适用于高并发、实时性要求高的场景。
优点
总结:高吞吐与低延迟,分布式与高可用,消息可靠,支持海量消息堆积,灵活的消息模型,稳定。
缺点
RocketMQ 是一款可用性高,可靠性强,适用于高并发、高吞吐场景的消息队列中间件,主要由 NameServer、Broker、Producer 和 Consumer 四部分组成。
RocketMQ 的优势在于可用性高、延迟性低、可拓展性强、消息可靠,具有亿级的消息堆积能力,支持灵活的消息模型,包括发布/订阅消息、定时/延迟消息、顺序消息、事务消息等。它尤其适用于电商和金融交易等要求高并发和高可靠的场景。
优点总结:高并发、低延迟、消息可靠、支持亿级消息堆积、支持灵活的消息模型,适用于高并发、高吞吐的电商、金融交易场景。
可以分为队列模型和发布-订阅模型。
队列模型指的就是 Producer 将消息放在队列中,由 Consumer(可以是 Consumer Group,引入轮询与负载均衡)。一旦 Consumer Group 将消息消费了,消息就不存在了,消费者之间是竞争关系(指的就是消息只能被一个 Consumer 消费,一经消费,消息就不存在了)。队列模型较为简单,是最基本的 MQ 类型,ActiveMQ、RabbitMQ、Redis List 等均属于队列模型。
发布-订阅模型中进一步引入了发布者(Publisher)、订阅者(Subscriber)和主题(Topic)三个概念。发布者负责生产和发送消息,它不直接将消息发送给订阅者,而是发送到消息队列组件(比如 RocketMQ 的 broker)上的主题当中;订阅者根据订阅模型(比如 RocketMQ 的 Pull/Push)从订阅的主题接收消息;主题是消息的管道,发布者将消息发送给特定的主题,订阅者从特定的主题接收消息。
发布-订阅模型可以概括为:订阅者向消息系统注册一个或多个感兴趣的主题,发布者将消息发送到特定的主题,消息系统负责将消息传递给所有订阅了该主题的订阅者,每个订阅者独立接收消息,彼此之间互不影响,也就是说一份消息可以被多次消费。
RocketMQ 采用发布-订阅模型。
消息的消费模式分为两种,分别是集群模式和广播模式。
默认情况下,消费者组采用集群消费,也就是一个 Consumer Group 当中的 Consumer 以竞争的形式消费 Topic 的 Queue 当中的下一条消息,一旦下一条消息被消费,就不会被重复消费了。
广播模式指的是 Topic 下的每一份消息都会发送给 Consumer Group 当中的每一个 Consumer 进行一次消费。
RocketMQ 的基本架构分为四部分,分别是 NameServer、Broker、Consumer 和 Producer。四者都采用集群模式部署,方便在分布式系统中快速拓展。
NameServer 是无状态服务器,角色类似于 Kafka 当中的 Zookeeper。它的特点和作用如下:
Broker 扮演消息存储与中转的角色。具体来说,Broker 的职责如下:
单个 Broker 与所有 NameServer 保持长连接,定时将 Topic 信息同步到 NameServer。
Producer 是 RocketMQ 中负责发送消息的客户端组件,它是消息的源头,可以将业务系统产生的消息发送到 Broker 服务器。具体来说,Producer 有三种消息发送的模式,分别是:
Producer 会自动从 NameServer 获取 Topic 路由信息,从而将 Message 发送到 Topic 所在的 Broker 服务器,因此 Producer 只需要知道 NameServer 的地址即可。
Consumer 是 RocketMQ 当中负责接收消息的客户端组件,它从 Broker 中根据 Topic 拉取消息交由业务线程处理,是消息队列中消息的最终目的地。
Consumer 有两种主要的消费模式,分别是:
可以从生产者、中间存储、消费者三个部分来对消息的可靠性进行保障。
生产者
Producer 可以设置请求确认机制,来确保消息被成功存储到 Broker:
中间存储
存储阶段,可以通过配置可靠性优先的 Broker 来确保消息的可靠性。「控制 Broker 的刷盘频率」
Broker 的主从模式
在这里简要了解一下 RocketMQ 的主从模式。Broker 通过主节点(Master)和从节点(Slave)的协同工作保障服务的可靠性。Master Broker 负责所有的写操作(消息写入、事务处理等),支持读操作。生产者直接讲消息发送到 Master,Master 同步或异步将消息发送给 Slave。Slave Broker 是 Master Broker 的热备份,仅承担读操作,不处理写请求。Master 宕机时,Slave 可升级为 Master。
RocketMQ 可以确保消息的可靠性,但是不能确保消息「准确地仅被消费一次」,因此需要在客户端做好接口的幂等性。常见的幂等性方案包括:
消费者水平扩容
增加消费者实例的数量,但是需要保证 Consumer Group 中的 Cosnumer 数量 ≤ \leq ≤ Queue 数量。「Topic 下有 8 个 Queue 则消费者组最多 8 个实例同时消费。如果消费者实例数 > \gt > Topic 下 Queue 的数量,则多余的实例会闲置」
动态扩容 Queue
临时增加 Topic 的 Queue 的数量。一个可选的方法如下:新建一个 Queue 数量更大的临时 Topic,然后使用转发 Consumer 从当前消息积压的 Topic 将消息转发到新的 Topic,再使用扩容后的 Consumer Group 来对临时 Topic 中的 Message 进行消费。消息积压处理完毕后再恢复正常。
Tag 是 Message 的二级标签(一级标签是 Topic),生产者可以为消息定义一个 Tag,消费者可以根据 Tag 订阅匹配的消息。基于 Tag 的过滤通常在 Broker 完成,避免不匹配的消息传输到消费者「消费者通过 subscribe 通知 broker 自己感兴趣的 Topic 和 Tag,所以 Broker 知道 Consumer 感兴趣的 Tag」。
基于 Tag 的消息过滤性能较高,可以处理 90% 的场景。但是局限性在于单个消息只能有一个 Tag。
生产者可以为 Message 设置自定义属性(Key-Value),消费者通过 SQL 表达式对感兴趣的消息进行过滤(即通过 SQL 对自定义属性进行筛选)。SQL 表达式过滤仍然 Broker 完成,支持数值、布尔、字符串等复杂条件。
SQL 表达式过滤的灵活性比较强,支持 >
,<
,LIKE
,IS NULL
等操作。但是需要 Broker 开启 SQL 过滤功能,且复杂的 SQL 可能增加 Broker CPU 的负载。
生产者可以为消息配置唯一 Key(比如订单 ID),消费者通过 Key 精准匹配。消息 Key 过滤在消费者端完成(需要先从 Broker 拉取消息),适用于精确查找的场景。
允许用户编写代码并部署到 Broker 的 Filter Server 上,实现完全自定义的过滤逻辑。适用于极复杂的过滤规则。
Filter Server 的灵活性最高,但是复杂性也最高,需要维护 Filter Server,且性能开销大,故生产环境慎用。
此处引用二哥的 Java 进阶之路的原话:
电商的订单超时自动取消就是一个典型的利用延时消息的例子,用户提交了一个订单,就可以发送一个延时消息,1h 后去检查这个订单的状态,如果还是未付款就取消订单释放库存。
RocketMQ 支持延时消息,只需要在生产消息的时候设置消息的延时级别。但目前 RocketMQ 支持的延时消息级别是有限的:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
。
临时存储 + 定时任务。
在写入阶段,延时消息将会被临时存储到内部 Topic SCHEDULE_TOPIC_XXXX
队列中,队列号对应着延迟级别。原始 Message 的 Topic 将会被写入这条临时存储的延时消息的属性当中(REAL_TOPIC=order_topic
)。
在延时到期投递阶段,Broker 启动后台线程 ScheduleMessageService
,按延迟级别扫描队列。到达触发时间的消息会被重新写入到原始 Topic 消费队列,供消费者拉取。
RocketMQ 通过事务消息实现分布式事务,其核心是半消息和**两阶段提交(2PC)**的协同工作。
半消息(Half Message)
RMQ_SYS_TRANS_HALF_TOPIC
的临时队列中;事务状态
步骤1:发送半消息
生产者发送标记为 TRAN_MSG
的半消息;
步骤2:执行本地事务
生产者实现 TransactionListener
处理本地事务;
步骤3:Broker 处理事务状态
checkLocalTransaction
查询事务状态。RocketMQ 的死信队列(Dead-Letter Queue,DLQ)是一种用于处理消费失败的容错机制。当消息多次重试后仍无法被成功消费时,会被自动转移到死信队列,避免消息丢失斌便于后续人工干预或分析,修复消息后可以重新发布。
核心作用
触发死信队列的条件
RECONSUME_LATER
状态且重试次数用尽。RocketMQ 的高可用主要体现在 Broker 采用主从 + 集群的模式。
Broker 主从有两种角色,分别是 Master 和 Slave。Master 负责读写,而 Slave 只读。也就是说 Producer 可以向 Master
写,而 Consumer 可以从 Master 和 Slave 读。通过同步或者异步复制来确保主从一致性。
读的高可用很好理解,Consumer 不需要知道哪个 Broker 是 Master 哪个 Broker 是 Slave,如果某个 Broker 繁忙,会自动转发读请求到其他 Broker。
写的高可用指的是在创建 Topic 时,Topic 自身的多个 Queue 会被分配到若干个 Broker 组上(相同 Broker 名称,不同 BrokerId 构成 Broker 组),当 Broker 组的 Master 不可用时,其他 Broker 组的 Master 仍然可用,就意味着当前 Topic 仍然有 Queue 可以写入,确保了读的高可用。