RocketMQ知识点总结

分享一些自己在准备RocketMQ八股面试的知识点整理。
参考:
RocketMQ官方文档:https://rocketmq.apache.org/zh/docs
JavaGudie二哥的java进阶之路

1、Rocketmq消息丢失

消息可能在哪些阶段丢失呢?可能会在这三个阶段发生丢失:生产阶段、存储阶段、消费阶段。
消息传递三阶段

在生产阶段,主要通过请求确认机制,来保证消息的可靠传递

  • 1、同步发送的时候,要注意处理响应结果和异常。如果返回响应OK,表示消息成功发送到了Broker,如果响应失败,或者发生其它异常,都应该重试。
  • 2、异步发送的时候,应该在回调方法里检查,如果发送失败或者异常,都应该进行重试。
  • 3、如果发生超时的情况,也可以通过查询日志的API,来检查是否在Broker存储成功。

存储阶段,可以通过配置可靠性优先的 Broker 参数来避免因为宕机丢消息,简单说就是可靠性优先的场景都应该使用同步。

  • 1、消息只要持久化到CommitLog(日志文件)中,即使Broker宕机,未消费的消息也能重新恢复再消费。
  • 2、Broker的刷盘机制:同步刷盘和异步刷盘,不管哪种刷盘都可以保证消息一定存储在pagecache中(内存中),但是同步刷盘更可靠,它是Producer发送消息后等数据持久化到磁盘之后再返回响应给Producer

不管异步还是同步,数据都能保证存在page cache,只是调用fsync的时机不同

从Consumer角度分析,如何保证消息被成功消费?

  • Consumer保证消息成功消费的关键在于确认的时机,不要在收到消息后就立即发送消费确认,而是应该在执行完所有消费业务逻辑之后,再发送消费确认。因为消息队列维护了消费的位置,逻辑执行失败了,没有确认,再去队列拉取消息,就还是之前的一条

如何保证客户端的高可用,两种场景:

  • 同步消费场景,业务代码手动发送CONSUME_SUCCESS ,保证 消息者的0丢失
  • 异步消费场景,需要通过业务维度的 终极0丢失保护措施:本地消息表+定时扫描 ,保证 消息者的0丢失

2、如何处理消息重复?

处理消息重复问题,主要有业务端自己保证,主要的方式有两种:业务幂等消息去重

业务幂等:第一种是保证消费逻辑的幂等性,也就是多次调用和一次调用的效果是一样的。这样一来,不管消息消费多少次,对业务都没有影响。

消息去重:第二种是业务端,对重复的消息就不再消费了。这种方法,需要保证每条消息都有一个惟一的编号,通常是业务相关的,比如订单号,消费的记录需要落库,而且需要保证和消息确认这一步的原子性。

具体做法是可以建立一个消费记录表,拿到这个消息做数据库的insert操作。给这个消息做一个唯一主键(primary key)或者唯一约束,那么就算出现重复消费的情况,就会导致主键冲突,那么就不再处理这条消息。

3、消息积压?

  • 消费者扩容:如果当前Topic的Message Queue的数量大于消费者数量,就可以对消费者进行扩容,增加消费者,来提高消费能力,尽快把积压的消息消费玩。
  • 消息迁移Queue扩容:如果当前Topic的Message Queue的数量小于或者等于消费者数量,这种情况,再扩容消费者就没什么用,就得考虑扩容Message Queue。可以新建一个临时的Topic,临时的Topic多设置一些Message Queue,然后先用一些消费者把消费的数据丢到临时的Topic,因为不用业务处理,只是转发一下消息,还是很快的。接下来用扩容的消费者去消费新的Topic里的数据,消费完了之后,恢复原状。

4、顺序消息?

本地锁(同时线程池里面只能有一个线程可以得到锁)+broker端同时锁住

部分顺序消息

部分顺序消息相对比较好实现,生产端需要做到把同 ID 的消息发送到同一个 Message Queue ;在消费过程中,要做到从同一个Message Queue读取的消息顺序处理——消费端不能并发处理顺序消息,这样才能达到部分有序。

实际上,每一个消费者的的消费端都是采用线程池实现多线程消费的模式,即消费端是多线程消费。虽然MessageListenerOrderly被称为有序消费模式,但是仍然是使用的线程池去消费消息。

MessageListenerOrderly的加锁机制:

1.消费者在进行某个队列的消息拉取时首先向Broker服务器申请队列锁,如果申请到锁,则拉取消息,否则放弃消息拉取,等到下一个队列负载周期(20s)再试。这一个锁使得一个MessageQueue同一个时刻只能被一个消费客户端消费,防止因为队列负载均衡导致消息重复消费。

2.假设消费者对messageQueue的加锁已经成功,那么会开始拉取消息,拉取到消息后同样会提交到消费端的线程池进行消费。但在本地消费之前,会先获取该messageQueue对应的锁对象,每一个messageQueue对应一个锁对象,获取到锁对象后,使用synchronized阻塞式的申请线程级独占锁。这一个锁使得来自同一个messageQueue的消息在本地的同一个时刻只能被一个消费客户端中的一个线程顺序的消费。

3.在本地加synchronized锁成功之后,还会判断如果是广播模式,则直接进行消费,如果是集群模式,则判断如果messagequeue没有锁住或者锁过期(默认30000ms),那么延迟100ms后再次尝试向Broker 申请锁定messageQueue,锁定成功后重新提交消费请求。

全局顺序消息

RocketMQ 默认情况下不保证顺序**,比如创建一个 Topic ,默认八个写队列,八个读队列,这时候一条消息可能被写入任意一个队列里**;在数据的读取过程中,可能有多个 Consumer ,每个 Consumer 也可能启动多个线程并行处理,所以消息被哪个 Consumer 消费,被消费的顺序和写人的顺序是否一致是不确定的。

要保证全局顺序消息, 需要先把 Topic 的读写队列数设置为 一,然后Producer Consumer 的并发设置,也要是一。简单来说,为了保证整个 Topic全局消息有序,只能消除所有的并发处理,各部分都设置成单线程处理 ,这时候就完全牺牲RocketMQ的高并发、高吞吐的特性了。

5、如何实现消息过滤?

消息过滤

  • 根据Tag过滤:这是最常见的一种,用起来高效简单
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_EXAMPLE");
consumer.subscribe("TOPIC", "TAGA || TAGB || TAGC");
  • SQL 表达式过滤:SQL表达式过滤更加灵活
consumer.subscribe("TopicTest", MessageSelector.bySql("a between 0 and 3");
  • Filter Server 方式:最灵活,也是最复杂的一种方式,允许用户自定义函数进行过滤

6、延迟队列

rocketmq的延迟是有等级的

private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
message.setDelayTimeLevel(3);

原理:每个时间段都有一个自己的messagequeue。先入对的一定先过期,所以按序放入对应的时间段队列即可

通过一个定时任务轮询这些队列,到期后,把消息投递到目标Topic的队列中,然后消费者就可以正常消费这些消息。

7、分布式事务,半消息?

如何解释分布式事务呢?事务大家都知道吧?要么都执行要么都不执行

如今比较常见的分布式事务实现有 2PC、TCC 和事务消息(half 半消息机制)

半消息:是指暂时还不能被 Consumer 消费的消息,Producer 成功发送到 Broker 端的消息,但是此消息被标记为 “暂不可投递” 状态,只有等 Producer 端执行完本地事务后经过二次确认了之后,Consumer 才能消费此条消息。

依赖半消息,可以实现分布式消息事务,其中的关键在于二次确认以及消息回查:

RocketMQ实现消息事务

  • 1、Producer 向 broker 发送半消息
  • 2、Producer 端收到响应,消息发送成功,此时消息是半消息,标记为 “不可投递” 状态,Consumer 消费不了。
  • 3、Producer 端执行本地事务。
  • 4、正常情况本地事务执行完成,Producer 向 Broker 发送 Commit/Rollback,如果是 Commit,Broker 端将半消息标记为正常消息,Consumer 可以消费,如果是 Rollback,Broker 丢弃此消息。
  • 5、异常情况,Broker 端迟迟等不到二次确认。在一定时间后,会查询所有的半消息,然后到 Producer 端查询半消息的执行情况。
  • 6、Producer 端查询本地事务的状态
  • 7、根据事务的状态提交 commit/rollback 到 broker 端。(5,6,7 是消息回查)
  • 8、消费者段消费到消息之后,执行本地事务,执行本地事务。

RocketMQ 中使用的是 事务消息加上事务反查机制 来解决分布式事务问题的。

img

8、死信队列

死信队列用于处理无法被正常消费的消息,即死信消息。

当一条消息初次消费失败,消息队列 RocketMQ 会自动进行消息重试;达到最大重试次数后,若消费依然失败,则表明消费者在正常情况下无法正确地消费该消息,此时,消息队列 RocketMQ 不会立刻将消息丢弃,而是将其发送到该消费者对应的特殊队列中,该特殊队列称为死信队列

死信消息的特点

  • 不会再被消费者正常消费。
  • 有效期与正常消息相同,均为 3 天,3 天后会被自动删除。因此,需要在死信消息产生后的 3 天内及时处理。

死信队列的特点

  • 一个死信队列对应一个 Group ID, 而不是对应单个消费者实例。
  • 如果一个 Group ID 未产生死信消息,消息队列 RocketMQ 不会为其创建相应的死信队列。
  • 一个死信队列包含了对应 Group ID 产生的所有死信消息,不论该消息属于哪个 Topic

RocketMQ 控制台提供对死信消息的查询、导出和重发的功能。

9、集群和高可用

Broker可以配置两种角色:Master和Slave,Master角色的Broker支持读和写,Slave角色的Broker只支持读,Master会向Slave同步消息。

也就是说Producer只能向Master角色的Broker写入消息,Cosumer可以从Master和Slave角色的Broker读取消息。

Consumer 的配置文件中,并不需要设置是从 Master 读还是从 Slave读,当 Master 不可用或者繁忙的时候, Consumer 的读请求会被自动切换到从 Slave。有了自动切换 Consumer 这种机制,当一个 Master 角色的机器出现故障后,Consumer 仍然可以从 Slave 读取消息,不影响 Consumer 读取消息,这就实现了读的高可用

在创建 Topic 的时候,把 Topic 的多个Message Queue 创建在多个 Broker 组上(相同 Broker 名称,不同 brokerId机器组成 Broker 组),这样当 Broker 组的 Master 不可用后,其他组Master 仍然可用, Producer 仍然可以发送消息 RocketMQ 目前还不支持把Slave自动转成 Master如果机器资源不足,需要把 Slave 转成 Master ,则要手动停止 Slave 色的 Broker ,更改配置文件,用新的配置文件启动 Broker

10、为什么不用zookeeper作为注册中心?

Kafka我们都知道采用Zookeeper作为注册中心——当然也开始逐渐去Zookeeper,RocketMQ不使用Zookeeper其实主要可能从这几方面来考虑:

1、基于性能的考虑,NameServer本身的实现非常轻量,而且可以通过增加机器的方式水平扩展,增加集群的抗压能力

2、持久化的机制来带的问题,ZooKeeper 的 ZAB 协议对每一个写请求,会在每个 ZooKeeper 节点上保持写一个事务日志,同时再加上定期的将内存数据镜像(Snapshot)

3、消息发送应该弱依赖注册中心,而RocketMQ的设计理念也正是基于此,生产者在第一次发送消息的时候从NameServer获取到Broker地址后缓存到本地,如果NameServer整个集群不可用,短时间内对于生产者和消费者并不会产生太大影响。这也可以作为kafka为什么要放弃其作为注册中心的原因

11、主要工作流程

工作流程:RocketMQ由NameServer注册中心集群、Producer生产者集群、Consumer消费者集群和若干Broker(RocketMQ进程)组成:

  1. Broker在启动的时候去向所有的NameServer注册,并保持长连接,每30s发送一次心跳
  2. Producer在发送消息的时候从NameServer获取Broker服务器地址,根据负载均衡算法选择一台服务器来发送消息
  3. Conusmer消费消息的时候同样从NameServer获取Broker地址,然后主动拉取消息来消费

12、RocketMQ是如何保存数据的呢?

RocketMQ主要的存储文件包括CommitLog文件、ConsumeQueue文件、Indexfile文件。

  • CommitLog:消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消息内容不是定长的。单个文件大小默认1G, 文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息主要是顺序写入日志文件,当文件满了,写入下一个文件。

  • ConsumeQueue:消息消费队列,引入的目的主要是提高消息消费的性能,由于RocketMQ是基于主题topic的订阅模式,消息消费是针对主题进行的,如果要遍历commitlog文件中根据topic检索消息是非常低效的。

Consumer即可根据ConsumeQueue来查找待消费的消息。其中,ConsumeQueue(逻辑消费队列)作为消费消息的索引,保存了指定Topic下的队列消息在CommitLog中的起始物理偏移量offset,消息大小size和消息Tag的HashCode值。

ConsumeQueue文件可以看成是基于Topic的CommitLog索引文件,故ConsumeQueue文件夹的组织方式如下:topic/queue/file三层组织结构

Comsumer Queue

  • IndexFile:IndexFile(索引文件)提供了一种可以通过key或时间区间来查询消息的方法。Index文件的存储位置是: {fileName},文件名fileName是以创建时的时间戳命名的,固定的单个IndexFile文件大小约为400M,一个IndexFile可以保存 2000W个索引,IndexFile的底层存储设计为在文件系统中实现HashMap结构,故RocketMQ的索引文件其底层实现为hash索引。

消息存储整体设计-来源官网

RocketMQ采用的是混合型的存储结构,即为Broker单个实例下所有的队列共用一个日志数据文件(即为CommitLog)来存储。

RocketMQ的混合型存储结构(多个Topic的消息实体内容都存储于一个CommitLog中)针对Producer和Consumer分别采用了数据和索引部分相分离的存储结构,Producer发送消息至Broker端,然后Broker端使用同步或者异步的方式对消息刷盘持久化,保存至CommitLog中。

只要消息被刷盘持久化至磁盘文件CommitLog中,那么Producer发送的消息就不会丢失。正因为如此,Consumer也就肯定有机会去消费这条消息。当无法拉取到消息后,可以等下一次消息拉取,同时服务端也支持长轮询模式,如果一个消息拉取请求未拉取到消息,Broker允许等待30s的时间,只要这段时间内有新消息到达,将直接返回给消费端。

RocketMQ的具体做法是,使用Broker端的后台服务线程—ReputMessageService不停地分发请求并异步构建ConsumeQueue(逻辑消费队列)和IndexFile(索引文件)数据

img

13、RocketMQ怎么对文件进行读写的?

RocketMQ对文件的读写巧妙地利用了操作系统的一些高效文件读写方式——PageCache顺序读写零拷贝

在RocketMQ中,ConsumeQueue逻辑消费队列存储的数据较少,并且是顺序读取,在page cache机制的预读取作用下,Consume Queue文件的读性能几乎接近读内存,即使在有消息堆积情况下也不会影响性能。而**对于CommitLog消息存储的日志数据文件来说,读取消息内容时候会产生较多的随机访问读取,**严重影响性能。如果选择合适的系统IO调度算法,比如设置调度算法为“Deadline”(此时块存储采用SSD的话),随机读的性能也会有所提升。

页缓存(PageCache)是OS对文件的缓存,用于加速对文件的读写。一般来说,程序对文件进行顺序读写的速度几乎接近于内存的读写速度,主要原因就是由于OS使用PageCache机制对读写访问操作进行了性能优化,将一部分的内存用作PageCache。对于数据的写入,OS会先写入至Cache内,随后通过异步的方式由pdflush内核线程将Cache内的数据刷盘至物理磁盘上。对于数据的读取,如果一次读取文件时出现未命中PageCache的情况,OS从物理磁盘上访问读取文件的同时,会顺序对其他相邻块的数据文件进行预读取。

  • 零拷贝

另外,RocketMQ主要通过MappedByteBuffer对文件进行读写操作。其中,利用了NIO中的FileChannel模型将磁盘上的物理文件直接映射到用户态的内存地址中(这种Mmap的方式减少了传统IO,将磁盘文件数据在操作系统内核地址空间的缓冲区,和用户应用程序地址空间的缓冲区之间来回进行拷贝的性能开销),将对文件的操作转化为直接对内存地址进行操作,从而极大地提高了文件的读写效率(正因为需要使用内存映射机制,故RocketMQ的文件存储都使用定长结构来存储,方便一次将整个文件映射至内存)。

所以,可以通过零拷贝的方式,减少用户态与内核态的上下文切换内存拷贝的次数,用来提升I/O的性能。零拷贝比较常见的实现方式是mmap,这种机制在Java中是通过MappedByteBuffer实现的。

底层是java所以还需要一次拷贝,rocketmq直接省去了。

mmap示意图-来源《图解操作系统》

14、RocketMQ的负载均衡?

RocketMQ中的负载均衡都在Client端完成,具体来说的话,主要可以分为Producer端发送消息时候的负载均衡和Consumer端订阅消息的负载均衡。

Producer的负载均衡

Producer端在发送消息的时候,会先根据Topic找到指定的TopicPublishInfo,在获取了TopicPublishInfo路由信息后,RocketMQ的客户端在默认方式下selectOneMessageQueue()方法会从TopicPublishInfo中的messageQueueList中选择一个队列(MessageQueue)进行发送消息。具这里有一个sendLatencyFaultEnable开关变量,如果开启,在随机递增取模的基础上,再过滤掉not available的Broker代理。

img

所谓的"latencyFaultTolerance",是指对之前失败的,按一定的时间做退避。例如,如果上次请求的latency超过550Lms,就退避3000Lms;超过1000L,就退避60000L;如果关闭,采用随机递增取模的方式选择一个队列(MessageQueue)来发送消息,latencyFaultTolerance机制是实现消息发送高可用的核心关键所在。

Consumer的负载均衡

对于PushConsumer和SimpleConsumer类型的消费者,默认且仅使用消息粒度负载均衡策略。

在RocketMQ中,Consumer端的两种消费模式都是基于拉模式来获取消息的,而在Push模式只是对pull模式的一种封装,其本质实现为消息拉取线程在从服务器拉取到一批消息后,然后提交到消息消费线程池后,又“马不停蹄”的继续向服务器再次尝试拉取消息。如果未拉取到消息,则延迟一下又继续拉取。在两种基于拉模式的消费方式中,均需要Consumer端知道从Broker端的哪一个消息队列中去获取消息。因此,有必要在Consumer端来做负载均衡,即Broker端中多个MessageQueue分配给同一个ConsumerGroup中的哪些Consumer消费。

  1. Consumer端的心跳包发送
  2. Consumer端实现负载均衡的核心类—RebalanceImpl

通过查看源码可以发现,RebalanceService线程的run()方法最终调用的是RebalanceImpl类的rebalanceByTopic()方法,这个方法是实现Consumer端负载均衡的核心。

rebalanceByTopic()方法会根据消费者通信类型为“广播模式”还是“集群模式”做不同的逻辑处理。

消息粒度负载均衡策略中,同一消费者分组内的多个消费者将按照消息粒度平均分摊主题中的所有消息,即同一个队列中的消息,可被平均分配给多个消费者共同消费。

消息粒度负载

队列粒度负载均衡策略中,同一消费者分组内的多个消费者将按照队列粒度消费消息,即每个队列仅被一个消费者消费。

队列级负载均衡原理

Topic下的消息消费队列、消费者Id排序,然后用消息队列分配策略算法(默认为:消息队列的平均分配算法),计算出待拉取的消息队列。这里的平均分配算法,类似于分页的算法,将所有MessageQueue排好序类似于记录,将所有消费端Consumer排好序类似页数,并求出每一页需要包含的平均size和每个页面记录的范围range,最后遍历整个range而计算出当前Consumer端应该分配到的的MessageQueue。下面这个就是队列粒度

Cosumer分配

消息消费队列在同一消费组不同消费者之间的负载均衡,其核心设计理念是在一个消息消费队列在同一时间只允许被同一消费组内的一个消费者消费,一个消息消费者能同时消费多个消息队列。

PS:

顺序消息负载机制

在顺序消息中,消息的顺序性指的是同一消息组内的多个消息之间的先后顺序。因此,顺序消息场景下,消息粒度负载均衡策略还需要保证同一消息组内的消息,按照服务端存储的先后顺序进行消费。不同消费者处理同一个消息组内的消息时,会严格按照先后顺序锁定消息状态,确保同一消息组的消息串行消费。 顺序消息负载策略

15、消息长轮询?

所谓的长轮询,就是Consumer 拉取消息,如果对应的 Queue 如果没有数据,Broker 不会立即返回,而是把 PullReuqest hold起来,等待 queue 有了消息后,或者长轮询阻塞时间到了,再重新处理该 queue 上的所有 PullRequest。

16、RocketMQ的消息刷盘机制

  • 同步刷盘:在消息达到Broker的内存之后,必须刷到commitLog日志文件中才算成功,然后返回Producer数据已经发送成功。
  • 异步刷盘:异步刷盘是指消息达到Broker内存后就返回Producer数据已经发送成功,会唤醒一个线程去将数据持久化到CommitLog日志文件中。

Broker 在消息的存取时直接操作的是内存(内存映射文件),这可以提供系统的吞吐量,但是无法避免机器掉电时数据丢失,所以需要持久化到磁盘中。

刷盘的最终实现都是使用NIO中的 MappedByteBuffer.force() 将映射区的数据写入到磁盘,如果是同步刷盘的话,在Broker把消息写到CommitLog映射区后,就会等待写入完成。

异步而言,只是唤醒对应的线程,不保证执行的时机,流程如图所示。

异步刷盘

17、indexFile和消息

  • IndexFile:IndexFile(索引文件)提供了一种可以通过key或时间区间来查询消息的方法。Index文件的存储位置是: {fileName},文件名fileName是以创建时的时间戳命名的,固定的单个IndexFile文件大小约为400M,一个IndexFile可以保存 2000W个索引,IndexFile的底层存储设计为在文件系统中实现HashMap结构,故RocketMQ的索引文件其底层实现为hash索引。
  • IndexFile文件示意图

RocketMQ中的每一条消息都包含了主题(Topic)、队列(Queue)和偏移量(Offset)等信息,将这些信息记录在IndexFile中可以建立快速检索的索引,提高消息的检索效率。IndexFile的存储结构如下图所示:

img

索引的生成

  1. 插入一条消息
  2. 计算出消息key的hashcode
  3. 根据hashcode%500w,计算出应该放到哪个槽中
  4. 然后在插入Index Item,并在槽中记录Index Item的位置

假设hash slot只有5个、index item 只有20个,演示一下插入索引的过程

img

插入key1, 计算出hashcode=5,5%5=0在hashslot的0位置

在index item list中插入一个Index Item

hashslot 0位置指向插入的Index item

img

插入key2, 计算出hashcode=7,7%5=2在hashslot的0位置

在index item list中插入一个Index Item

hashslot 2位置指向插入的Index item

img

插入key3, 计算出hashcode=10,10%5=0在hashslot的0位置

此时发生了hash碰撞,它在slot的位置和key1相同,怎么处理?

在index item list中插入一个Index Item

hashslot 0位置指向插入的Index item key3

img

那key1岂不是变成一个孤岛了?

Index item key3会将它的preIndexNo指向Index item key1,这样index item key3和index item key1形成了一个单向链表

img

18、Consumer的两种消费模式push/pull

push模式让人感觉由broker将消息主动push到consumer。但在实际实现时,**这两种模式都采用了类似长轮询(long polling)的机制,即由Consumer主动向Broker拉取消息。**不过,它们的具体行为有所不同,我们主要介绍一下Push模式。

Push模式:
在Push模式下,Consumer通过DefaultMQPushConsumer类与Broker建立连接,并保持心跳。Consumer定期向Broker发送请求获取消息,但Broker并不会立即返回空响应,而是会挂起Consumer的请求,直到有新消息到达或者达到预设的最大等待时间(例如5秒)才返回消息。

19、广播模式,集群模式

生产者和消费者的模式通常包括集群模式和广播模式。这两种模式分别用于不同的场景,具有不同的特点和优势。

在集群模式下,多个相同角色的实例组成一个集群,它们共同协作以提供服务。对于生产者和消费者而言,集群模式有以下特点:

生产者: 生产者将消息发送到整个集群,集群内的任意一个节点都可以接收和处理消息。

消费者: 当使用集群消费模式时,RocketMQ 认为任意一条消息只需要被消费组内的任意一个消费者处理即可。每个消息只会被消费者组内的一个实例消费。

在广播模式下消息会被发送到所有的订阅者,每个订阅者都会接收到相同的消息。对于生产者和消费者而言,广播模式有以下特点:

生产者: 生产者将消息发送到所有订阅该主题的消费者。

消费者: 消费者组内的每个实例都会接收相同的消息,每个消息都会被所有的消费者实例处理。因此即使扩缩消费者数量也无法提升或降低消费能力。
要实时通知所有消费者的场景,如广告推送、实时通知等。 不需要负载均衡,每个消费者都需要处理所有消息。

20、同步复制、异步复制

上面的同步刷盘和异步刷盘是在单个结点层面的,而同步复制和异步复制主要是指的 Borker 主从模式下,主节点返回消息给客户端的时候是否需要同步从节点。

  • 同步复制:也叫 “同步双写”,也就是说,只有消息同步双写到主从节点上时才返回写入成功
  • 异步复制:消息写入主节点之后就直接返回写入成功

相关推荐

  1. Apache RocketMQ知识表格总结及示例

    2024-04-15 00:48:02       10 阅读
  2. 【SpringBoot】知识总结

    2024-04-15 00:48:02       42 阅读
  3. JVM知识总结

    2024-04-15 00:48:02       36 阅读
  4. Qt知识总结

    2024-04-15 00:48:02       35 阅读
  5. Docker知识总结

    2024-04-15 00:48:02       15 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-15 00:48:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-15 00:48:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-15 00:48:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-15 00:48:02       18 阅读

热门阅读

  1. C++ 中的运算符优先级

    2024-04-15 00:48:02       14 阅读
  2. 每日练习——leetcode454和4

    2024-04-15 00:48:02       15 阅读
  3. leetcode48 旋转图像

    2024-04-15 00:48:02       17 阅读
  4. Kotlin 面试题

    2024-04-15 00:48:02       14 阅读
  5. 前端面试问题汇总 - 工程管理工具篇

    2024-04-15 00:48:02       15 阅读
  6. 前端面试问题汇总 - 其他问题

    2024-04-15 00:48:02       15 阅读
  7. c++智能指针(2) -- auto_ptr

    2024-04-15 00:48:02       17 阅读
  8. Elasticsearch 支持的插件 —— 筑梦之路

    2024-04-15 00:48:02       17 阅读