RocketMQ特性及面试(下)

5,160 阅读9分钟

八、主动同步策略:同步双写、异步复制

8.1 多 Master 多 Slave 模式,异步复制

每个 Master 配置一个 Slave,有多对Master-Slave,HA 采用异步复制方式,主备有短暂消息延迟,毫秒级。

  • 优点:即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,因为 Master 宕机后,消费者仍然可以从 Slave 消费,此过程对应用透明。不需要人工干预。性能同多 Master 模式几乎一样。

  • 缺点:Master 宕机,磁盘损坏情况,会丢失少量消息。

8.2 多 Master 多 Slave 模式,同步双写

每个 Master 配置一个 Slave,有多对Master-Slave,HA 采用同步双写方式,主备都写成功,向应用返回成功。

  • 优点:数据与服务都无单点,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高

  • 缺点:性能比异步复制模式略低,大约低 10%左右,发送单个消息的 RT 会略高。目前主宕机后,备机不能自动切换为主机,后续会支持自动切换功能。

九、消息的刷盘策略

9.1 异步刷盘(ASYNC_FLUSH)

返回成功状态时,消息只是被写入内存 pagecache,写操作返回快,吞吐量达,当内存里的消息积累到一定程度时,统一出发写磁盘动作,快速写入。

在有 RAID 卡,SAS 15000 转磁盘测试顺序写文件,速度可以达到 300M 每秒左右,而线上的网卡一般都为千兆网卡,写磁盘速度明显快于数据网络入口速度,那举是否可以做到写完内存就吐用户返回,由后台线程刷盘呢?

由于磁盘速度大于网卡速度,那么刷盘的进度肯定可以跟上消息的写入速度。 万一由于此时系统压力过大,可能堆积消息,除了写入 IO,还有读取 IO,万一出现磁盘读取落后情况, 会不会导致系统内存溢出,答案是否定的,原因如下: a) 写入消息到 pagecache 时,如果内存不足,则尝试丢弃干净的 page,腾出内存供新消息使用,策略是 LRU 方式。 b) 如果干净页不足,此时写入 pagecache 会被阻塞,系统尝试刷盘部分数据,大约每次尝试 32 个 page,来找出更多干净 page。

综上,内存溢出的情况不会出现。

9.2 同步刷盘(SYNC_FLUSH)

返回成功状态时,消息已经被写入磁盘。

消息写入内存 pagecache 后,立即通知刷盘线程,刷盘完成后,返回消息写成功的状态。

同步刷盘与异步刷盘的唯一区别是异步刷盘写完 pagecache 直接返回,而同步刷盘需要等待刷盘完成才返回, 同步刷盘流程如下:

  1. 写入 pagecache 后,线程等待,通知刷盘线程刷盘。
  2. 刷盘线程刷盘后,唤醒前端等待线程,可能是一批线程。
  3. 前端等待线程吐用户返回成功。

十、消息持久性

RocketMQ收到消息后,会将消息持久化到文件,并利用Linux文件系统内存来提高性能

十一、消息存储

11.1 RocketMQ存储模型:

RocketMQ的消息的存储是由ConsumeQueue和CommitLog 配合来完成的,ConsumeQueue中只存储很少的数据,消息主体都是通过CommitLog来进行读写。

CommitLog: 是消息主体以及元数据的存储主体,对CommitLog建立一个ConsumeQueue,每个ConsumeQueue对应一个(概念模型中的)MessageQueue,所以只要有Commit Log在,Consume Queue即使数据丢失,仍然可以恢复出来。 Consume Queue: 是一个消息的逻辑队列,存储了这个Queue在CommitLog中的起始offset,log大小和MessageTag的hashCode。每个Topic下的每个Queue都有一个对应的ConsumerQueue文件,例如Topic中有三个队列,每个队列中的消息索引都会有一个编号,编号从0开始,往上递增。并由此一个位点offset的概念,有了这个概念,就可以对Consumer端的消费情况进行队列定义。

  • RocketMQ的broker端,不负责推送消息,无论消费者是否消费消息,都将消息存储起来。谁要消费消息,就向broker发请求获取消息,消费记录由consumer来维护。RocketMQ提供了两种存储方式来保留消费记录:一种是保留在consumer所在的服务器上;另一种是保存在broker服务器上。用户还可以自己实现相应的消费进度存储接口。

  • 默认情况下,采用集群消费(CLUSTERING),会将记录保存在broker端;而采用广播消费(BROADCASTING)则会将消费记录保存在本地。

  • RocketMQ以Topic来管理不同应用的消息。对于生产者而言,发送消息是,需要指定消息的Topic,对于消费者而言,在启动后,需要订阅相应的Topic,然后可以消费相应的消息。Topic是逻辑上的概念,在物理实现上,一个Topic由多个Queue组成,采用多个Queue的好处是可以将Broker存储分布式化,提高系统性能。

  • RocketMQ中,producer将消息发送给Broker时,需要制定发送到哪一个队列中,默认情况下,producer会轮询的将消息发送到每个队列中(所有broker下的Queue合并成一个List去轮询)。

  • 对于consumer而言,会为每个consumer分配固定的队列(如果队列总数没有发生变化),consumer从固定的队列中去拉取没有消费的消息进行处理。

11.2 RocketMQ存储特点:

零拷贝原理:Consumer 消费消息过程,使用了零拷贝,零拷贝包含以下两种方式:

  1. 使用 mmap + write 方式
    • 优点:即使频繁调用,使用小块文件传输,效率也很高
    • 缺点:不能很好的利用 DMA 方式,会比 sendfile 多消耗CPU,内存安全性控制复杂,需要避免 JVM Crash 问题。
  2. 使用 sendfile 方式
    • 优点:可以利用 DMA 方式,消耗 CPU 较少,大块文件传输效率高,无内存安全新问题。
    • 缺点:小块文件效率低亍 mmap 方式,只能是 BIO 方式传输,不能使用 NIO。 RocketMQ 选择了第一种方式,mmap+write 方式,因为有小块数据传输的需求,效果会比 sendfile 更好。

11.3 RocketMQ 数据存储结构:

十二、消息堆积

Producer 已经将消息发送到消息队列 RocketMQ 的服务端,但由于 Consumer 消费能力有限,未能在短时间内将所有消息正确消费掉,此时在消息队列 RocketMQ 的服务端保存着未被消费的消息,该状态即消息堆积。

支持10亿级别的消息堆积,不会因为消息堆积影响性能。
如果出现消息堆积并且性能明显下降的情况,首先查看RocketMQ控制台,查看消费者状态找寻性能下降主机,查看堆栈信息,之后查看 ConsumeMessageThread 的状态与堆栈。

十三、消息可靠性

生产者的可靠性保证:生产者发送消息后返回SendResult,如果isSuccess返回true,则表示消息已经确认发送到服务器并被服务器接收保存。整个发送过程是一个同步过程。

服务器的可靠性:消息生产者发送的消息,RocketMQ服务收到后在做必要的校验和检查之后马上保存到磁盘,写入成功后返回给生产者。因此可以确认每条发送结果为成功的消息都会被消息服务器写入磁盘。

消费者的可靠性:消费者是一条一条顺序消费的,之后在成功消费一条后才会消费吓一跳。如果在消费某一条消息时失败则会重试消费这条消息,默认为5次,如果超过最大次数仍然无法消费,则将消息保存到本地,后台线程继续重试消费,主线程则会继续往后走,消费队列后面的消息。

十四、消息过滤

Consumer 可以根据消息标签(Tag)对消息进行过滤,确保 Consumer 最终只接收被过滤后的消息类型。消息过滤在消息队列 RocketMQ 的服务端完成。

以下图电商交易场景为例,从客户下单到收到商品这一过程会生产一系列消息,比如订单创建消息(order)、支付消息(pay)、物流消息(logistics)。 这些消息会发送到 Topic 为 Trade_Topic 的队列中,被各个不同的系统所接收,比如支付系统、物流系统、交易成功率分析系统、实时计算系统等。 其中,物流系统只需接收物流类型的消息(logistics),而实时计算系统需要接收所有和交易相关(order、pay、logistics)的消息。

十五、削峰填谷

流量削锋也是消息队列 RocketMQ 的常用场景,一般在秒杀或团队抢购活动中使用广泛。 在秒杀或团队抢购活动中,由于用户请求量较大,导致流量暴增,秒杀的应用在处理如此大量的访问流量后,下游的通知系统无法承载海量的调用量,甚至会导致系统崩溃等问题而发生漏通知的情况。为解决这些问题,可在应用和下游通知系统之间加入消息队列 RocketMQ,如下图所示。

秒杀处理流程如下所述:

  1. 用户发起海量秒杀请求到秒杀业务处理系统。
  2. 秒杀处理系统按照秒杀处理逻辑将满足秒杀条件的请求发送至消息队列 RocketMQ。
  3. 下游的通知系统订阅消息队列 RocketMQ 的秒杀相关消息,再将秒杀成功的消息发送到相应用户。
  4. 用户收到秒杀成功的通知。

大规模机器的缓存同步

双十一大促时,各个分会场会有玲琅满目的商品,每件商品的价格都会实时变化。使用缓存技术也无法满足对商品价格的访问需求,缓存服务器网卡满载。访问较多次商品价格查询影响会场页面的打开速度。 此时需要提供一种广播机制,一条消息本来只可以被集群的一台机器消费,如果使用消息队列 RocketMQ 的广播消费模式,那么这条消息会被所有节点消费一次,相当于把价格信息同步到需要的每台机器上,取代缓存的作用。