RocketMQ简介与使用

mq的特点与作用

在这里插入图片描述

应用解耦

在这里插入图片描述

削峰

在这里插入图片描述

数据分发

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

mq的优点和缺点

在这里插入图片描述

角色介绍

在这里插入图片描述

概念

在这里插入图片描述

NameServer

很多时候称为命名发现服务,其在RocketMQ中起着中转承接的作用,是一个无状态的服务,多个NameServer之间不通信。任何Producer,Consumer,Broker与所有NameServer通信,向NameServer请求或者发送数据。而且都是单向的,Producer和Consumer请求数据,Broker发送数据。正是因为这种单向的通信,RocketMQ水平扩容变得很容易。

在这里插入图片描述

分组(Group)

  • 生产者:标识发送同一类消息的Producer,通常发送逻辑一致。发送普通消息的时候,仅标识使用,并无特别用处。主要作用用于事务消息:
    (事务消息中如果某条发送某条消息的producer-A宕机,使得事务消息一直处于PREPARED状态并超时,则broker会回查同一个group的其它producer,确认这条消息应该 commit 还是 rollback)

  • 消费者:标识一类 Consumer 的集合名称,这类 Consumer 通常消费一类消息,且消费逻辑一致。同一个 Consumer Group 下的各个实例将共同消费 topic的消息,起到负载均衡的作用。

  • 消费进度以 Consumer Group 为粒度管理,不同 Consumer Group 之间消费进度彼此不受影响,即消息 A 被 Consumer Group1 消费过,也会再给 Consumer Group2 消费。

主题(Topic)

标识一类消息的逻辑名字,消息的逻辑管理单位。无论消息生产还是消费,都需要指定 Topic。

  • 区分消息的种类;一个发送者可以发送消息给一个或者多个 Topic。
  • 一个消息的接收者可以订阅一个或者多个 Topic 消息。
  • 一个topic可以存在于多台broker,一个broker可以有多个topic,一个topic可以有多个Queue
    在这里插入图片描述

标签(Tag)

  • RocketMQ 支持给在发送的时候给 topic 打 tag,同一个 topic 的消息虽然逻辑管理是一样的。但是消费 topic1的时候,如果你消费订阅的时候指定的是 tagA,那么 tagB 的消息将不会投递。
  • RocketMQ设计消息过滤来解决无意义的传输,即对于客户端不需要的消息,Broker就不会传输给客户端,以免浪费带宽。
  • 为什么服务端采用tag的hashcode过滤而不是tag过滤?
    因为tag的比较速度相比于tag的hashcode的比较速度要慢。hashcode对比是数字比较,底层可以直接采用位运算,而字符串只能按字符顺序比较,所以服务端采用tag的hashcode过滤。但是这样只能过滤掉绝大多数不关心的tag消息,因为tag可能存在hash碰撞,所以在客户端还需要进行tag值过滤。

Broker

在这里插入图片描述

CommitLog

当生产者发送一个消息到Broker的时候,Broker接收到一条消息会把这个消息直接写到磁盘上的一个日志文件,这个文件叫做CommitLog,直接顺序写入这个CommitLog

  • (一个Broker=顺序多个CommitLog)
  • 单个文件大小默认1G ,Broker收到消息之后直接追加写入这个文件的末尾,如果一个CommitLog写满1GB,就会创建一个新的CommitLog日志。
    在这里插入图片描述

commitlog文件删除

触发过期文件删除的条件:

1.达到配置的时间点

2.磁盘用了超过85%

3.手动执行

消息队列(Message Queue)

简称 Queue 或 Q。消息物理管理单位。一个 Topic 将有若干个 Q。若一个 Topic 创建在不同的 Broker,则不同的 broker 上都有若干 Q,消息将物理的存储在不同 Broker 结点上,具有水平扩展的能力。

  • 无论生产者还是消费者,实际的生产和消费都是针对 Q 级别。
  • 例如 Producer 发送消息的时候,会预先选择(默认轮询)好该 Topic 下面的某一条 Q发送;
  • Consumer 消费的时候也会负载均衡地分配若干个 Q,只拉取对应 Q 的消息。
  • 当Broker接受到一条消息写入CommitLog之后,其实同时也会将这条数据的在CommitLog中存储的物理位置,也就是一个文件偏移量,就是offset写入这条消息所处的MessageQueue对应ConsumeQueue文件中,这个文件存储了实际消息的索引信息。
  • ConsumeQueue中的存储的这个物理位置(偏移量offset)其实就是对CommitLog文件的一个消息引用。其实ConsumeQueue文件中不只是存了消息引用,还包含了消息的长度,以及tag,还有hashcode,一条数据是20个字节,每个ConsumeQueue文件保存30万条数据,大概每个文件是5.72MB,写满会创建新的文件。
  • 每一条 message queue 均对应一个索引文件。并且即使文件被删除,也能通过消息文件(commit log)恢复回来。

Message Queue 文件删除:

deleteExpiredFileByOffset方法从第一个consumeQueue开始遍历,拿最后一个offset获取其物理点位,并比较当前commitlog中最小的物理点位。如果小了,则把这个comsumequeue删除。

其次遍历所有的cosumequeue,并从第一个offset开始,直到发现其指定的最小的物理点位>=当前commitlog中最小的物理点位

偏移量(Offset)

RocketMQ 中,有很多 offset 的概念。一般我们只关心暴露到客户端的 offset。不指定的话,就是指 Message Queue 下面的 offset。

  • Message queue 是无限长的数组。一条消息进来下标就会涨 1,而这个数组的下标就是 offset,Message queue 中的 max offset 表示消息的最大 offset
  • Consumer offset 可以理解为标记 Consumer Group 在一条逻辑 Message Queue 上,消息消费到哪里即消费进度。但从源码上看,这个数值是消费过的,最新待消费的消息是 offset+1,即表示的是下次拉取的 offset

消费者offset的管理与存储

1.offset远程管理模式
当消费模式为集群消费时,offset使用远程模式管理。因为所有Cosnumer实例对消息采用的是均衡消费,所有Consumer共享Queue的消费进度。
Consumer在集群消费模式下offset相关数据以json的形式持久化到Broker磁盘文件中,Broker启动时会加载这个文件,并写入到一个双层Map(ConsumerOffsetManager)。外层map的key为topic@group,value为内层map。内层map的key为queueId,value为offset。

map<topic@group,map<queueId,offset>> ConsumerOffsetManager

当发生Rebalance时,新的Consumer会从该Map中获取到相应的数据来继续消费。
集群模式下offset采用远程管理模式,主要是为了保证Rebalance机制。

2.offset本地管理模式
当消费模式为广播消费时,offset使用本地模式存储。因为每条消息会被所有的消费者消费,每个消费者管理自己的消费进度,各个消费者之间不存在消费进度的交集。
Consumer在广播消费模式下offset相关数据以json的形式持久化到Consumer本地磁盘文件中

集群

在 RocketMQ 中,有主从架构和 Dledger 两种高可用方案:

第一种通过主 Broker 将消息发送到从 Broker 实现高可用,在主 Broker IO 压力大或宕机的时候,从 Broker 可以接管读请求,但这种方案不支持在主 Broker 宕机后自动进行故障转移,且从 Broker 不支持写请求,也就是说在主 Broker 宕机后我们只能手动处理。

第二种是在 RocketMQ 4.5.X 的时候才加入的新的方案,其为基于 Raft 算法实现的一个高可用方案,支持集群自动选主与故障转移,但 TPS 低于第一种方案。
在这里插入图片描述
在这里插入图片描述

Master如何同步到Slave

  • 所有的Slave也会向所有的NameServer进行注册,也会30s发送心跳。
  • RocketMQ自身的Master-Slave模式采取的是Pull模式拉取信息,也就是Slave Broker不停的发送请求到Master Broker去拉取信息

RocketMQ实现读写分离了吗?

  • 原则上不算是完全的读写分离,生产者发送消息是往Master进行发送,而消费者获取消息则有可能是Master有可能是Slave,要根据Master Broker的负载情况和Slave Broker的同步情况判断。

  • 如果Master Broker负载很重,已经要抗10w写并发,那么此时Master Broker就会建议你从Slave Broker中拉取消息,还有就是Slave同步的比较慢,100w数据差了4w,而消费者可能都获取完96w了,那么下次还是只能从Master Broker去拉取消息,因为同步太慢,没法获取更新的消息了。

Broker的Master或者Slave挂掉了有什么影响

1.Master Broker宕机
主从架构

  • 这个时候Slave Broker有一样的数据在的,只不过可能会有部分数据没来得及同步过来,而且不能自动切换成Master Broker。
  • 一旦Master故障,那么就需要手动做一些运维操作,将Slave重新修改参数配置,重启机器调整为Master Broker,所以这种Master-Slave模式不是彻底的高可用模式,没法自动切换主从。

Dledger

  • 这个机制是基于Raft协议实现,简单来说把Dledger融入RocketMQ之后,可以让一个Master Broker对应多个Slave Broker,也就是一份数据有多份备份,一旦Master宕机了,那么就可以在多个Slave中,通过Dledger技术和Raft协议算法选举处leader,然后直接将一个Slave Broker选举成新的Master Broker,整个过程也许只要10s或者几十秒就可以自动完成。
  • 但是吞吐量没有老机制高

2.Slave Broker宕机
会有一点影响,但是不大,因为写入全部发送到Master Broker中,然后获取也是可以走Master Broker的,所以整体影响不大,只不过会导致读写压力都集中在Master Broker上。

双主双从集群架构图

在这里插入图片描述

(重点)集群工作流程

在这里插入图片描述

生产者

消息发送样例

在这里插入图片描述
同步发送(Sync Send) 同步发送是指发送消息后,等待服务器返回响应结果后再进行其他操作的发送方式。
优点:

  • 可靠性高:发送者收到服务器确认消息后,可以确保消息已经被服务器接收并存储。
  • 简单易懂:同步发送的逻辑相对简单,容易理解和实现。

缺点:

  • 性能影响:同步发送会在发送者端阻塞,直到收到服务器的响应,这可能会影响系统的吞吐量。
  • 延迟较高:特别是在网络状况不佳或服务器处理压力大的情况下,同步发送可能会导致较高的延迟。

异步发送(Async Send) 异步发送是指发送消息后,不需要等待服务器返回响应,而是通过回调的方式来处理发送结果的发送方式。

优点:

  • 非阻塞:发送者不会因等待响应而阻塞,可以立即进行其他操作,提高了系统的吞吐量。
  • 灵活性高:通过回调处理发送结果,可以灵活地处理各种情况,如消息发送失败、重复发送等。
    缺点:
  • 复杂性增加:异步发送需要处理回调逻辑,增加了系统的复杂性。
  • 可靠性问题:如果回调处理不当,可能会导致消息丢失或处理不及时。

单向发送(Oneway Send) 单向发送是指发送消息后,不等待服务器响应,也不需要回调来处理发送结果的发送方式。
优点:

  • 性能最佳:单向发送无需等待响应和处理回调,是三种方式中性能最好的。
  • 延迟最低:因为没有等待和回调的处理,所以单向发送的延迟最低。
    缺点:
  • 可靠性最低:发送者无法确认消息是否已经被服务器接收,可能会出现消息丢失的情况。
  • 错误处理困难:如果消息发送失败,由于没有回调,发送者难以进行错误处理。

当发送的消息不重要,采用one-way方式(没有任何返回结果),以提高吞吐量;
当发送的消息很重要,且对响应时间不敏感的时候采用sync(同步发送)方式;(顺序消费,相当于一个接口按顺序一个人一个人的去调)
当发送的消息很重要,且对响应时间非常敏感的时候采用async(异步发送)方式;(相当于一个接口所有人一起调用)

1.发送同步消息

在这里插入图片描述

2.发送异步消息

在这里插入图片描述
在这里插入图片描述

3.发送单项消息

在这里插入图片描述

发送批量消息

在这里插入图片描述
分割有工具类可以操作

producer 消息发送分区策略(负载均衡)

producer使用长连接每30S向NameServer查询Topic列表信息

producer在发送消息的时候,会选择一个messageQueue,然后会将消息发送到messageQueue所在的broker

  • 默认规则:Producer 会轮流向topic里各个Message Queue 发送消息
  • 自定义:自定义消息分发策略,通过实现 MessageQueueSelector,选择指定队列发送消息
  • 在这里插入图片描述

消费者

本质上消费者是一个线程池进行消息的消费,默认参数是20个核心线程

Consumer分区分配策略(负载均衡)

只有集群模式才有负载均衡,Consumer是以MessageQueue为单位来进行负载均衡

Consumer使用长连接每30S向NameServer查询Topic路由信息

负载均衡时机

主动负载均衡:

  1. 启动时立即进行负载均衡
  2. 定时(默认 20s)负载均衡一次

被动负载均衡(收到 broker 通知):

  1. 客户端上下线
  2. 订阅关系变化:订阅新 topic 或有旧的 topic 不再订阅

负载均衡算法:

  • AllocateMessageQueueAveragely :平均分配算法(默认)
  • AllocateMessageQueueAveragelyByCircle:环状分配消息队列
  • AllocateMessageQueueByConfig:按照配置来分配队列: 根据用户指定的配置来进行负载
  • AllocateMessageQueueByMachineRoom:按照指定机房来配置队列
  • AllocateMachineRoomNearby:按照就近机房来配置队列:
  • AllocateMessageQueueConsistentHash:一致性hash,根据消费者的cid进行

Rebalance机制

集群模式才有的机制
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

负载均衡源码解析

Consumer端的心跳包发送

  • 在Consumer启动后,它就会通过定时任务不断地向RocketMQ集群中的所有Broker实例发送心跳包(其中包含了,消息消费分组名称、订阅关系集合、消息通信模式和客户端id的值等信息)。
  • Broker端在收到Consumer的心跳消息后,会将它维护在ConsumerManager的本地缓存变量—consumerTable,同时并将封装后的客户端网络通道信息保存在本地缓存变量—channelInfoTable中,为之后做Consumer端的负载均衡提供可以依据的元数据信息。

Consumer端实现负载均衡的核心类—RebalanceImpl

  • 在Consumer实例的启动流程中的启动MQClientInstance实例部分,会完成负载均衡服务线程—RebalanceService的启动(每隔20s执行一次)。
  • 通过查看源码可以发现,RebalanceService线程的run()方法最终调用的是RebalanceImpl类的rebalanceByTopic()方法,该方法是实现Consumer端负载均衡的核心。
  • 这里,rebalanceByTopic()方法会根据消费者通信类型为“广播模式”还是“集群模式”做不同的逻辑处理。

这里主要来看下集群模式下的主要处理流程:

(1) 从rebalanceImpl实例的本地缓存变量—topicSubscribeInfoTable中,获取该Topic主题下的消息消费队列集合(mqSet);

(2) 根据topic和consumerGroup为参数调用mQClientFactory.findConsumerIdList()方法向Broker端发送获取该消费组下消费者Id列表的RPC通信请求(Broker端基于前面Consumer端上报的心跳包数据而构建的consumerTable做出响应返回,业务请求码:GET_CONSUMER_LIST_BY_GROUP);

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

(4) 然后,调用updateProcessQueueTableInRebalance()方法,具体的做法是,先将分配到的消息队列集合(mqSet)与processQueueTable做一个过滤比对,更新自己需要消费队列的数据。

总结:
1、从namesrv获取messageQueue信息
2、从broker获取consumer信息
3、选择Rebalance策略
4、三者结合实现Rebalance操作,获取到自己所要消费的Q

消息消费样例

  • 消费者应用一般是分布式系统,以集群方式部署。消费者在订阅Topic时,可根据实际业务选择集群消费或广播消费,集群中的多个消费者将按照实际选择的消费模式消费消息。
  • 使用相同Group ID的消费者属于同一个集群。同一个集群下的消费者消费逻辑必须完全一致(包括Tag的使用)。

在这里插入图片描述

1.负载均衡模式(集群模式)

消费者采用负载均衡方式消费消息,一个分组(Group)下的多个消费者共同消费队列消息,每个消费者处理的消息不同。一个Consumer Group中的各个Consumer实例分摊去消费消息,即一条消息只会投递到一个Consumer Group下面的一个实例。
在这里插入图片描述

在这里插入图片描述

2.广播模式

广播消费模式中消息将对一个Consumer Group下的各个Consumer实例都投递一遍。即使这些 Consumer属于同一个Consumer Group,消息也会被Consumer Group 中的每个Consumer都消费一次。实际上,是一个消费组下的每个消费者实例都获取到了topic下面的每个Message Queue去拉取消费。所以消息会投递到每个消费者实例。
在这里插入图片描述

在这里插入图片描述

3.阿里云设置

在这里插入图片描述

consumer 消费方式

MQ中Pull和Push的两种消费方式
对于任何一款消息中间件而言,消费者客户端一般有两种方式从消息中间件获取消息并消费。

(1)Pull方式

由消费者客户端主动向消息中间件(MQ消息服务器代理)拉取消息;采用Pull方式,如何设置Pull消息的频率需要重点去考虑,举个例子来说,可能1分钟内连续来了1000条消息,然后2小时内没有新消息产生(概括起来说就是“消息延迟与忙等待”)。如果每次Pull的时间间隔比较久,会增加消息的延迟,即消息到达消费者的时间加长,MQ中消息的堆积量变大;若每次Pull的时间间隔较短,但是在一段时间内MQ中并没有任何消息可以消费,那么会产生很多无效的Pull请求的RPC开销,影响MQ整体的网络性能。
在这里插入图片描述

(2)Push方式

严格意义上来讲,RocketMQ并没有实现PUSH模式,而是对拉模式进行一层包装,名字虽然是 Push 开头,实际在实现时,使用 Pull 方式实现。通过 Pull 不断不断不断轮询 Broker 获取消息

如果broker主动推送消息的话有可能push速度快,消费速度慢的情况,那么就会造成消息在consumer端堆积过多,同时又不能被其他consumer消费的情况。而pull的方式可以根据当前自身情况来pull,不会造成过多的压力而造成瓶颈。所以采取了pull的方式

批量消费原理

批量消费主要分为以下两个阶段:

  • 消息从生产者发布至消息队列RocketMQ版后,Push消费者中的拉消息线程通过长轮询将消息拉到后台缓存。
  • Push消费者根据缓存情况是否满足任一批量条件(每次批量消费的最大消息数量,等待时间),判断是否将消息提交给消费线程完成消费。 消费者内置线程池进行消费

如果不配置参数使用push方式 每次批量消费的最大消息数量默认为1,现在一般都是默认使用这种方式

功能优势及场景示例
批量消费的功能优势和场景示例说明如下:

  • 优势一:提高消息的吞吐能力和处理效率
    场景示例:上游订单系统和下游Elasticsearch系统间通过消息队列RocketMQ版解耦,Elasticsearch消费订单系统的10条日志消息,每一条消息对于Elasticsearch系统而言都是一次RPC请求,假设一次RPC请求耗时10毫秒,那么不使用批量消费的耗时为10×10=100毫秒;理想状态下,使用批量消费的耗时可缩短至10毫秒,因为10条消息合并为一次消费,大大提高消息的处理效率。

  • 优势二:降低下游资源的API调用频率
    场景示例:给数据库中插入数据,每更新一条数据执行一次插入任务,如果数据更新较频繁,可能会对数据库造成较大压力。此时,您可以设置每10条数据批量插入一次或每5秒执行一次插入任务,降低系统运行压力。
    在这里插入图片描述

消费者参数配置

在这里插入图片描述

特殊消息

顺序消息

在kafka中,消息可以通过自定义分区策略来实现消息的顺序发送,实现原理就是把同一类消息都发送到相同的分区上。

在RocketMQ中,一个broker中存在多个Queue,把同一类消息发送到同一个Queue上就行

在这里插入图片描述
在这里插入图片描述

延迟消息

延时消息使用场景
采用RocketMQ的延时消息可以实现定时任务的功能,而无需使用定时器。使用场景主要有:

电商交易系统的订单超时未支付,自动取消订单
在电商交易系统中,像淘宝、京东,我们提交了一个订单之后,在支付时都会提示,需要在指定时间内(例如30分钟)完成支付,否则订单将被取消的消息,实际上这个超时未支付功能就可以使用延时消息来实现。在下单成功之后,就发送一个延时消息,然后指定消息的延时时间为30分钟,这条消息将会在30分钟后投递给后台业务系统(Consumer),此时才能被消费者进行消费,消费消息的时候会再去检查这个订单的状态,确认下是否支付成功,如果支付成功,则忽略不处理;如果订单还是未支付,则进行取消订单、释放库存等操作;

在这里插入图片描述
在这里插入图片描述

过滤消息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

事务消息

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

RocketMQ架构层面速度快的原因

顺序写CommitLog

  • RocketMQ将所有的消息写入转为顺序写,所有topic的消息顺序写入一个commitLog存储文件,文件大小满足设置后就新建一个。也就是说,仅仅将消息数据追加到文件的末尾,不是在文件的随机位置来修改数据。
  • 并且建立消息对应的 CosumerQueue ,然后消费者是通过 CosumerQueue 得到消息的真实物理地址再去 CommitLog 获取消息的。可以将 CosumerQueue 理解为消息的索引。

通过mmap内存映射技术将磁盘中的commitLog文件映射到内存中,减少拷贝次数

传统读写文件:
在这里插入图片描述
使用 mmap 读写文件:
在这里插入图片描述

  • mmap是一种将文件映射到虚拟内存的技术,可以将文件在磁盘位置的地址和在虚拟内存中的虚拟地址通过映射对应起来,之后就可以在内存这块区域进行读写数据,而不必调用系统级别的read,wirte这些函数,从而提升IO操作性能

  • mq中单个 commitlog 文件,默认大小为 1G,而一个MappedFile即为内存映射中的封装的一个 CommitLog 文件,消息写入时会存在MappedFile的MappdByteBuffer中,在调用force()的时候将数据刷到磁盘中

RocketMQ是如何基于mmap技术+page cache技术优化的?

  • RocketMQ底层对CommitLog、ConsumeQueue之类的磁盘文件的读写操作,基本上都会采用mmap技术来实现。

  • 具体到代码层面,就是基于JDK NIO包下的MappedByteBuffer的map()函数,将一个磁盘文件(比如一个CommitLog文件,或者是一个ConsumeQueue文件)映射到内存里来

这里我必须给大家解释一下,这个所谓的内存映射是什么意思:
其实有的人可能会误以为是直接把那些磁盘文件里的数据给读取到内存里来了,类似这个意思,但是并不完全是对的。因为刚开始你建立映射的时候,并没有任何的数据拷贝操作,其实磁盘文件还是停留在那里,只不过他把物理上的磁盘文件的一些地址和用户进程私有空间的一些虚拟内存地址进行了一个映射

在这里插入图片描述

  • 这个地址映射的过程,就是JDK NIO包下的MappedByteBuffer.map()函数干的事情,底层就是基于mmap技术实现的。

另外这里给大家说明白的一点是,这个mmap技术在进行文件映射的时候,一般有大小限制,在1.5GB~2GB之间
所以RocketMQ才让CommitLog单个文件在1GB,ConsumeQueue文件在5.72MB,不会太大。
这样限制了RocketMQ底层文件的大小,就可以在进行文件读写的时候,很方便的进行内存映射了。

PageCache,实际上在这里就是对应虚拟内存 PageCache=虚拟内存
在这里插入图片描述
接下来就可以对这个已经映射到内存里的磁盘文件进行读写操作了
写入消息到CommitLog文件:

  1. 先把一个CommitLog文件通过MappedByteBuffer的map()函数映射其地址到你的虚拟内存地址。

  2. 接着对这个MappedByteBuffer执行写入操作了,写入的时候他会直接进入PageCache中,然后过一段时间之后,由os的线程异步刷入磁盘中,如下图。
    在这里插入图片描述
    上面的图里,只有一次数据拷贝的过程,就是从PageCache里拷贝到磁盘文件里而已!使用mmap技术之后,相比于传统磁盘IO的一个性能优化,减少了拷贝的次数。

从磁盘文件里读取数据

  1. 首先会判断一下,当前你要读取的数据是否在PageCache里?如果在,就直接从PageCache里读取出来!

比如刚写入CommitLog的数据还在PageCache里,此时你Consumer来消费肯定是从PageCache里读取数据的。

  1. 如果PageCache里没有你要的数据,那么此时就会从磁盘文件里加载数据到PageCache中去,而且PageCache技术在加载数据的时候,还会将你加载的数据块的临近的其他数据块也一起加载到PageCache里去。
    在这里插入图片描述
    仅仅发生了一次拷贝,而不是两次拷贝,所以这个性能相较于传统IO来说,肯定又是提高了。

异步刷盘

在Linux实现层面,并非是直接将数据写入磁盘的实现,而是写入Linux操作系统中的os page cache里,也就是仅仅写入内存中,并标记该page cache为脏页,接下来由操作系统自己决定什么时候把os cache里的数据真的刷入(flush)到磁盘文件中。所以写磁盘的操作变为了写内存的操作,性能上得到了提升。

  • 同步刷盘
    每次发送消息,消息都直接存储在 MappedFile 的 mappdByteBuffer,然后直接调用 force() 方法刷写到磁盘,等到 force 刷盘成功后,再返回给调用方(GroupCommitRequest#waitForFlush)就是其同步调用的实现,本质上是使用CountdownLatch来实现的同步等待及唤醒
    在这里插入图片描述
    异步刷盘
    分为两种情况,是否开启堆外内存缓存池,具体配置参数:MessageStoreConfig#transientStorePoolEnable
    在这里插入图片描述
    1)transientStorePoolEnable=false(默认)
    消息追加时,直接存入 MappedByteBuffer(pageCache) 中,然后定时 flush,这种很好理解,和同步刷盘相比,只是依赖定时器来定时将MappedByteBuffer中的消息数据刷入磁盘中,流程与同步刷盘差不多
    2)transientStorePoolEnable = true
    如果设置堆外缓冲池transientStorePoolEnable参数为true,就有点不一样了,之前提到过消息会写入MappedByteBuffer中,也就是pageCache中,而实际上读数据也是读的pageCache,如果读写并发较大,实际也有一定性能影响,mq通过将消息写到堆外内存中来提高性能,实现写消息、读消息读写分离。同时通过堆外缓冲池的使用,也能减少Java堆内存的使用,减少GC压力
    开启该参数情况下,消息在追加时,先放入到堆外内存writeBuffer 中,然后定时 commit 到 FileChannel,然后定时flush

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值