RocketMQ之原理深入教学

源码 2024-10-6 22:26:15 88 0 来自 中国
1 RocketMQ

1.1 为什么要选RocketMQ

总结一下:
选择中心件的可以从这些维度来思量:可靠性,性能,功能,可运维行,可拓展性,社区活泼度。现在常用的几个中心件,ActiveMQ作为“老古董”,市面上用的已经不多,别的几种:

  • RabbitMQ:
    优点:轻量,迅捷,容易摆设和利用,拥有灵活的路由设置
    缺点:性能和吞吐量不太抱负,不易举行二次开发
  • RocketMQ:
    优点:性能好,高吞吐量,稳固可靠,有活泼的中文社区
    缺点:兼容性上不是太好
  • Kafka:
    优点:拥有强盛的性能及吞吐量,兼容性很好
    缺点:由于“攒一波再处置惩罚”导致延伸比力高
1.2 RocketMQ优缺点

RocketMQ优点:

  • 单机吞吐量:十万级
  • 可用性:非常高,分布式架构
  • 消息可靠性:颠末参数优化设置,消息可以做到0丢失
  • 功能支持:MQ功能较为美满,还是分布式的,扩展性好
  • 支持10亿级别的消息堆积,不会由于堆积导致性能降落
  • 源码是Java,方便联合公司本身的业务二次开发
  • 天生为金融互联网范畴而生,对于可靠性要求很高的场景,尤其是电商内里的订单扣款,以及业务削峰,在大量交易业务涌入时,后端可能无法及时处置惩罚的情况
  • RocketMQ在稳固性上可能更值得信任,这些业务场景在阿里双11已经履历了多次磨练
RocketMQ缺点:

  • 支持的客户端语言不多,现在是Java及c++,此中c++不成熟
  • 没有在MQ核心中去实现JMS等接口,有些体系要迁徙须要修改大量代码
1.3 消息模子

1.3.1 消息队列模子

消息队列有两种模子:队列模子和发布/订阅模子

  • 队列模子
    这是最初的一种消息队列模子,对应着消息队列发-存-收的模子。生产者往某个队列内里发送消息,一个队列可以存储多个生产者的消息,一个队列也可以有多个消耗者,但是消耗者之间是竞争关系,也就是说每条消息只能被一个消耗者消耗。


  • 发布/订阅模子
    假如须要将一份消息数据分发给多个消耗者,而且每个消耗者都要求收到全量的消息。很显然,队列模子无法满意这个需求。办理的方式就是发布/订阅模子。
    在发布 - 订阅模子中,消息的发送方称为发布者(Publisher),消息的吸取方称为订阅者(Subscriber),服务端存放消息的容器称为主题(Topic)。发布者将消息发送到主题中,订阅者在吸取消息之前须要先订阅主题。“订阅”在这里既是一个动作,同时还可以以为是主题在消耗时的一个逻辑副本,每份订阅中,订阅者都可以吸取到主题的全部消息。
    3.png
它和 队列模式的异同:生产者就是发布者,队列就是主题,消耗者就是订阅者,无本质区别。唯一的差异点在于:一份消息数据是否可以被多次消耗
1.3.2 RocketMQ消息模子

RocketMQ利用的消息模子是标准的发布-订阅模子,在RocketMQ的术语表中,生产者、消耗者和主题,与发布-订阅模子中的概念是完全一样的。
1.3.3 RocketMQ中成员

RocketMQ本身的消息是由下面几部门构成:

4.png
1.3.3.1 Message

Message(消息)就是要传输的信息
一条消息必须有一个主题(Topic),主题可以看做是你的信件要邮寄的所在。
一条消息也可以拥有一个可选的标签(Tag)和额处的键值对,它们可以用于设置一个业务Key 并在 Broker 上查找此消息以便在开发期间查找问题。
1.3.3.2 Topic

Topic(主题)可以看做消息的归类,它是消息的第一级范例。比如一个电商体系可以分为:交易业务消息、物流消息等,一条消息必须有一个 Topic
Topic 与生产者和消耗者的关系非常松散,一个 Topic 可以有0个、1个、多个生产者向其发送消息,一个生产者也可以同时向差异的 Topic 发送消息。
一个 Topic 也可以被 0个、1个、多个消耗者订阅。
1.3.3.3 Tag

Tag(标签)可以看作子主题,它是消息的第二级范例,用于为用户提供额外的灵活性。利用标签,同一业务模块差异目的的消息就可以用雷同 Topic 而差异的 Tag 来标识。比如交易业务消息又可以分为:交易业务创建消息、交易业务完成消息等,一条消息可以没有 Tag
标签有助于保持你的代码干净和连贯,而且还可以为 RocketMQ 提供的查询体系提供资助。
1.3.3.4 Group


  • Consumer Group :
    RocketMQ中,订阅者的概念是通过消耗组(Consumer Group)来体现的。每个消耗组都消耗主题中一份完备的消息,差异消耗组之间消耗进度相互不受影响,也就是说,一条消息被Consumer Group1消耗过,也会再给Consumer Group2消耗。
    消耗组中包罗多个消耗者,同一个组内的消耗者是竞争消耗的关系,每个消耗者负责消耗组内的一部门消息。默认情况,假如一条消息被消耗者Consumer1消耗了,那同组的其他消耗者就不会再收到这条消息。
  • Producer Group :
    生产者组,简朴来说就是多个发送同一类消息的生产者称之为一个生产者组,一群Topic雷同的Producer
1.3.3.5 Message Queue

Message Queue(消息队列),一个 Topic 下可以设置多个消息队列,Topic 包括多个 Message Queue ,假如一个 Consumer 须要获取 Topic下全部的消息,就要遍历全部的 Message Queue。
RocketMQ尚有一些别的的Queue——比方ConsumerQueue
1.3.3.6 Offset

在Topic的消耗过程中,由于消息须要被差异的组举行多次消耗,以是消耗完的消息并不会立即被删除,这就须要RocketMQ为每个消耗组在每个队列上维护一个消耗位置(Consumer Offset),这个位置之前的消息都被消耗过,之后的消息都没有被消耗过,每乐成消耗一条消息,消耗位置就加一
也可以这么说,Queue 是一个长度无穷的数组,Offset 就是下标。
1.3.3.7 总结图示

RocketMQ的消息模子中,这些就是比力关键的概念了
画张图总结一下


1.4 消息的消耗模式

消息消耗模式有两种:Clustering(集群消耗)和Broadcasting(广播消耗)

6.png
默认情况下就是集群消耗,这种模式下一个消耗者组共同消耗一个主题的多个队列,一个队列只会被一个消耗者消耗,假如某个消耗者挂掉,分组内别的消耗者会接替挂掉的消耗者继承消耗。
而广播消耗消息会发给消耗者组中的每一个消耗者举行消耗。
1.5 RoctetMQ根本架构

先看图,RocketMQ的根本架构

7.png
RocketMQ 一共有四个部门构成:NameServer,Broker,Producer 生产者,Consumer 消耗者,它们对应了:发现、发、存、收,为了包管高可用,一样平常每一部门都是集群摆设的
类比一下我们生存的邮政体系——
邮政体系要正常运行,离不开下面这四个脚色, 一是发信者,二 是收信者, 三是负责暂存传输的邮局, 四是负责调和各个地方邮局的管理机构。对应到 RocketMQ 中,这四个脚色就是 Producer、 Consumer、 Broker、NameServer


1.5.1 NameServer

NameServer 是一个无状态的服务器,脚色雷同于 Kafka利用的 Zookeeper,但比 Zookeeper 更轻量。
特点:
每个 NameServer 结点之间是相互独立,相互没有任何信息交互。
Nameserver 被计划成险些是无状态的,通过摆设多个结点来标识本身是一个伪集群,Producer 在发送消息前从 NameServer中获取 Topic 的路由信息也就是发往哪个 Broker,Consumer 也会定时从 NameServer获取 Topic的路由信息,Broker 在启动时会向 NameServer 注册,并定时举行心跳毗连,且定时同步维护的 Topic 到 NameServer
功能紧张有两个:

  • 和Broker 结点保持长毗连。
  • 维护 Topic 的路由信息。
1.5.2 Broker

消息存储和中转脚色,负责存储和转发消息
Broker 内部维护着一个个 Consumer Queue,用来存储消息的索引,真正存储消息的地方是 CommitLog(日记文件)
单个 Broker 与全部的 Nameserver 保持着长毗连和心跳,并会定时将 Topic 信息同步到 NameServer,和 NameServer 的通信底层是通过 Netty 实现的。
1.5.3 Producer

消息生产者,业务端负责发送消息,由用户自行实现和分布式摆设。
Producer由用户举行分布式摆设,消息由Producer通过多种负载均衡模式发送到Broker集群,发送低延时,支持快速失败。
RocketMQ 提供了三种方式发送消息:同步、异步和单向

  • 同步发送:同步发送指消息发送方发出数据后会在收到吸取方发反相应之后才发下一个数据包。一样平常用于紧张关照消息,比方紧张关照邮件、营销短信。
  • 异步发送:异步发送指发送方发出数据后,不等吸取方发反相应,接着发送下个数据包,一样平常用于可能链路耗时较长而对相应时间敏感的业务场景,比方用户视频上传后关照启动转码服务。
  • 单向发送:单向发送是指只负责发送消息而不等候服务器回应且没有回调函数触发,实用于某些耗时非常短但对可靠性要求并不高的场景,比方日记收集
1.5.4 Consumer

消息消耗者,负责消耗消息,一样平常是配景体系负责异步消耗。
Consumer也由用户摆设,支持PUSH和PULL两种消耗模式,支持集群消耗和广播消耗,提供及时的消息订阅机制。

  • Pull:拉取型消耗者(Pull Consumer)主动从消息服务器拉取信息,只要批量拉取到消息,用户应用就会启动消耗过程,以是 Pull 称为主动消耗型
  • Push:推送型消耗者(Push Consumer)封装了消息的拉取、消耗进度和其他的内部维护工作,将消息到达时实行的回调接口留给用户应用步调来实现。以是 Push  称为被动消耗范例,但实在从实现上看还是从消息服务器中拉取消息,差异于 Pull 的是 Push  起首要注册消耗监听器,当监听器处触发后才开始消耗消息
2 原理

2.1 RocketMQ团体工作流程

简朴来说,RocketMQ是一个分布式消息队列,也就是消息队列+分布式体系
作为消息队列,它是发-存-收的一个模子,对应的就是Producer、Broker、Cosumer;作为分布式体系,它要有服务端、客户端、注册中心,对应的就是Broker、Producer/Consumer、NameServer
以是我们看一下它紧张的工作流程:RocketMQ由NameServer注册中心集群、Producer生产者集群、Consumer消耗者集群和多少Broker(RocketMQ历程)构成:

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

2.2 为什么RocketMQ不利用Zookeeper作为注册中心

Kafka我们都知道接纳Zookeeper作为注册中心——固然也开始渐渐去Zookeeper,RocketMQ不利用Zookeeper实在紧张可能从这几方面来思量:

  • 基于可用性的思量
    根据CAP理论,同时最多只能满意两个点,而Zookeeper满意的是CP,也就是说Zookeeper并不能包管服务的可用性,Zookeeper在举行推举的时间,整个推举的时间太长,期间整个集群都处于不可用的状态,而这对于一个注册中心来说肯定是不能继承的,作为服务发现来说就应该是为可用性而计划。
  • 基于性能的思量
    NameServer本身的实现非常轻量,而且可以通过增长呆板的方式水平扩展,增长集群的抗压本事,而Zookeeper的写是不可扩展的,Zookeeper要办理这个问题只能通过分别范畴,分别多个Zookeeper集群来办理,起首操纵起来太复杂,其次如许还是又违背了CAP中的A的计划,导致服务之间是不连通的。
  • 长期化的机制来带的问题
    ZooKeeper的 ZAB 协议对每一个写哀求,会在每个ZooKeeper节点上保持写一个变乱日记,同时再加上定期的将内存数据镜像(Snapshot)到磁盘来包管数据的划一性和长期性,而对于一个简朴的服务发现的场景来说,这实在没有太大的须要,这个实现方案太重了。而且本身存储的数据应该是高度定制化的。
  • 消息发送应该弱依靠注册中心
    RocketMQ的计划理念也正是基于此,生产者在第一次发送消息的时间从NameServer获取到Broker所在后缓存到本地,假如NameServer整个集群不可用,短时间内对于生产者和消耗者并不会产生太大影响。
2.3 Broker生存数据(CommitLog,ConsumeQueue,Indexfile)

RocketMQ紧张的存储文件包括CommitLog文件、ConsumeQueue文件、Indexfile文件


11.png

  • CommitLog:消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消息内容不是定长的。单个文件巨细默认1G, 文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件巨细为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息紧张是序次写入日记文件,当文件满了,写入下一个文件。
    CommitLog文件生存于${Rocket_Home}/store/commitlog目次中,从图中我们可以显着看出来文件名的偏移量,每个文件默认1G,写满后主动天生一个新的文件。

    12.png
  • ConsumeQueue:消息消耗队列,引入的目的紧张是进步消息消耗的性能,由于RocketMQ是基于主题topic的订阅模式,消息消耗是针对主题举行的,假如要遍历commitlog文件中根据topic检索消息黑白常低效的。
    Consumer即可根据ConsumeQueue来查找待消耗的消息。此中,ConsumeQueue(逻辑消耗队列)作为消耗消息的索引,生存了指定Topic下的队列消息在CommitLog中的起始物理偏移量offset,消息巨细size和消息Tag的HashCode值
    ConsumeQueue文件可以看成是基于Topic的CommitLog索引文件,故ConsumeQueue文件夹的构造方式如下:topic/queue/file三层构造结构,详细存储路径为:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}
    同样ConsumeQueue文件接纳定长计划,每一个条目共20个字节,分别为8字节的CommitLog物理偏移量、4字节的消息长度、8字节tag hashcode,单个文件由30W个条目构成,可以像数组一样随机访问每一个条目,每个ConsumeQueue文件巨细约5.72M;


  • 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(索引文件)数据。

15.png
2.4 RocketMQ怎么对文件举行读写

RocketMQ对文件的读写奇妙地利用了操纵体系的一些高效文件读写方式——PageCache、序次读写、零拷贝
2.4.1 PageCache、序次读取

在RocketMQ中,ConsumeQueue逻辑消耗队列存储的数据较少,而且是序次读取,在page cache机制的预读取作用下,Consume Queue文件的读性能险些靠近读内存,纵然在有消息堆积情况下也不会影响性能。而对于CommitLog消息存储的日记数据文件来说,读取消息内容时间会产生较多的随机访问读取,严峻影响性能。假如选择符合的体系IO调理算法,比如设置调理算法为Deadline(此时块存储接纳SSD的话),随机读的性能也会有所提拔。
页缓存(PageCache)是OS对文件的缓存,用于加速对文件的读写。一样平常来说,步调对文件举行序次读写的速率险些靠近于内存的读写速率,紧张缘故起因就是由于OS利用PageCache机制对读写访问操纵举行了性能优化,将一部门的内存用作PageCache。对于数据的写入,OS会先写入至Cache内,随后通过异步的方式由pdflush内核线程将Cache内的数据刷盘至物理磁盘上。对于数据的读取,假如一次读取文件时出现未掷中PageCache的情况,OS从物理磁盘上访问读取文件的同时,会序次对其他相邻块的数据文件举行预读取
2.4.2 零拷贝

RocketMQ紧张通过MappedByteBuffer对文件举行读写操纵。此中,利用了NIO中的FileChannel模子将磁盘上的物理文件直接映射到用户态的内存所在中(这种Mmap的方式淘汰了传统IO,将磁盘文件数据在操纵体系内核所在空间的缓冲区,和用户应用步调所在空间的缓冲区之间来回举行拷贝的性能开销),将对文件的操纵转化为直接对内存所在举行操纵,从而极大地进步了文件的读写服从(正由于须要利用内存映射机制,故RocketMQ的文件存储都利用定长结构来存储,方便一次将整个文件映射至内存)。
什么是零拷贝
在操纵体系中,利用传统的方式,数据须要履历频频拷贝,还要履历用户态/内核态切换

  • 从磁盘复制数据到内核态内存;
  • 从内核态内存复制到用户态内存;
  • 然后从用户态内存复制到网络驱动的内核态内存;
  • 末了是从网络驱动的内核态内存复制到网卡中举行传输。
以是,可以通过零拷贝的方式,淘汰用户态与内核态的上下文切换和内存拷贝的次数,用来提拔I/O的性能。零拷贝比力常见的实现方式是mmap,这种机制在Java中是通过MappedByteBuffer实现的。


2.5 消息刷盘怎么实现

RocketMQ提供了两种刷盘计谋:同步刷盘和异步刷盘

  • 同步刷盘:在消息到达Broker的内存之后,必须刷到commitLog日记文件中才算乐成,然后返回Producer数据已经发送乐成。
  • 异步刷盘:异步刷盘是指消息到达Broker内存后就返回Producer数据已经发送乐成,会叫醒一个线程去将数据长期化到CommitLog日记文件中
Broker在消息的存取时直接操纵的是内存(内存映射文件),这可以提供体系的吞吐量,但是无法制止呆板掉电时数据丢失,以是须要长期化到磁盘中
刷盘的终极实现都是利用NIO中的 MappedByteBuffer.force() 将映射区的数据写入到磁盘,假如是同步刷盘的话,在Broker把消息写到CommitLog映射区后,就会等候写入完成
异步而言,只是叫醒对应的线程,不包管实行的时机,流程如图所示。

18.png
2.6 RocketMQ的负载均衡

RocketMQ中的负载均衡都在Client端完成,详细来说的话,紧张可以分为Producer端发送消息时间的负载均衡和Consumer端订阅消息的负载均衡。
2.6.1 Producer的负载均衡

Producer端在发送消息的时间,会先根据Topic找到指定的TopicPublishInfo,在获取了TopicPublishInfo路由信息后,RocketMQ的客户端在默认方式下selectOneMessageQueue()方法会从TopicPublishInfo中的messageQueueList中选择一个队列(MessageQueue)举行发送消息。具这里有一个sendLatencyFaultEnable开关变量,假如开启,在随机递增取模的底子上,再过滤掉not available的Broker署理。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-12-4 01:58, Processed in 0.215575 second(s), 36 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表