RocketMQ篇一(基本概念、集群组成、部署类型、存储机制(介质、结构、刷盘机制、主从复制)、消息防丢失、负载均衡)


1、messageDelayLevel 是设置重试时间间隔还是延时队列时间间隔的?
messageDelayLevel 是设置延时队列时间间隔的。消息异常重试时也有用到。
2、默认重试次数和时间间隔是多少?
默认情况下都是重试16次,使用延时等级配置的时间。

Apache RocketMQ开发者指南

1. 基本概念

RocketMQ集群主要有四大组成部分:NameServer、Broker、Producer、Consumer。

  • NameServer:无状态,动态列表,这也是和ZooKeeper的重要区别之一。ZooKeeper是有状态的。
  • Producer:消息生产者,负责发消息到Broker。
  • Consumer:消息消费者,负责从Broker上拉取消息进行消费,消费完进行ack。
  • Broker:负责收发消息、存储消息。Broker在实际部署过程中对应一台服务器,每个Broker可以存储多个Topic的消息,每个Topic的消息也可以分片存储于不同的Broker中。MessageQueue用于存储消息的物理地址,每个Topic中的消息地址存储于多个MessageQueue中,Consumer Group由多个Consumer实例构成。

RocketMQ有以下几个优点:

  1. 其代码结构清晰优秀,底层采用Netty框架进行NIO通信。
  2. 内部使用轻量级的NameServer进行服务发现和动态路由,提高了服务性能,并且支持消息失败重试机制。
  3. 天然地支持集群模式、消费者负载均衡广播消费等特性、水平扩展能力很强、吞吐量大。
  4. 采用零拷贝的原理、顺序写盘,支持亿级消息堆积能力。
  5. 提供丰富的消息机制,如顺序消息、事务消息、延迟消息等。

RocketMQ的消息都是pull,虽然有push类,但是底层实现采用的是长轮询机制,即拉取方式。Broker端属性longPollingEnable标记是否开启长轮询。默认开启。

1.1 消息生产者(Producer)

消息生产者,负责生产发送消息,一般由业务系统负责产生并发送消息到Broker。投递的过程支持快速失败并且低延迟。

RocketMQ有三种方式发送消息:同步、异步、单向。同步和异步方式均需要Broker返回确认信息,单向发送不需要。

  • 同步发送:Producer发送消息到主Broker之后之后得等到Broker主从复制完毕之后才算成功。一般用于重要消息。Producer 向 broker 发送消息,阻塞当前线程等待Broker响应发送结果。
  • 异步发送:Producer发送消息到主Broker之后之后不等Broker主从复制完毕,只需主Broker返回成功即算发送成功。一般用于链路耗时长而对响应时间敏感的业务场景。
  • OneWay单向发送:Producer发送消息后不管。适用于耗时短但对可靠性要求不高的场景,例如日志收集。

生产者中,会把同一类Producer组成一个集合,叫做生产者组,这类Producer发送同一类消息且发送逻辑一致。如果发送的是事务消息且原始生产者在发送之后崩溃,则Broker服务器会联系同一生产者组的其他生产者实例以提交或回溯消费。

1.2 消息消费者(Consumer)

消息消费者,负责消费消息,一版是业务系统负责异步消费。一个消息消费者会从Broker服务器拉取消息、并将其提供给业务系统。从业务系统的角度而言提供了两种消费方式:Pull拉取式消费、Push推动式消费。

  • 拉取式消费的应用通常主动调用Consumer的拉消息方法从Broker服务器拉消息、主动权由应用控制。一旦获取了批量消息,应用就会启动消费过程。
  • 推动式消费模式下Broker收到数据后会主动推送给消费端,该消费模式一般实时性较高。

Message:消息,就是要传输的信息,一个消息必须有一个主题(Topic),还可以有一个可选的标签(Tag)和额外的键值对。
Topic:主题,表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行消息订阅的基本单位。同一个Topic下的数据,会分片保存到不同的Broker上,而每一个分片单位,就叫做MessageQueue。MessageQueue是生产者发送消息与消费者消费消息的最小单位。
Tag:标签,可以看做子主题,是消息的第二级类型,同一个业务模块不同目的的消息可以用相同Topic而不同的Tag来标识。一条消息可以没有Tag。
Group:分组,一个组可以订阅多个Topic。分为ProducerGroup、ConsumerGroup,代表某一类的生产者和消费者,一般来说统一
MessageQueue:消息队列,主题被划分为一个或多个子主题,即消息队列。一个Topic下可以设置多个消息队列,发送消息时执行该消息的Topic,RocketMQ会轮训该Topic下的所有队列将消息发出去。

1.3 代理服务器(Broker Server)

消息中转角色,负责存储消息,转发消息。Broker在RocketMQ系统中负责接收从生产者发送来的消息并存储、同时为消费者的拉取请求作准备。Broker也存储消息相关的元数据,包括消费者组、消费进度偏移和主题和队列消息等。

  • Broker是具体提供业务的服务器,单个Broker节点与所有的NameServer节点保持长连接与心跳,并会定时将Topic信息注册到NameServer。
  • Broker负责消息存储,以Topic为纬度支持轻量级的队列,单机可以支撑上万队列规模,支持消息推拉模型。

Broker Server是RocketMQ真正饿业务核心,包含了多个重要的子模块:

  • Romoting Module:整个Broker的实体,负责处理来自Clients端的请求。
  • Clients Manager:负责管理客户端(Producer/Consumer)和维护Consumer的Topic订阅信息。
  • Store Service:提供方便简单的API接口处理消息存储到物理硬盘和查询功能。
  • HA Service:高可用服务,提供Master Broker和Slave Broker之间的数据同步功能。
  • Index Service:根据特定的Message Key对投递到Broker的消息进行索引服务,以提供消息的快速查询。

1.4 名字服务(NameServer)

NameServer充当路由消息的提供者。Broker会在启动时向所有的NameServer注册自己的服务信息,并且后续通过心跳请求的方式保证这个服务信息的实时性。生产者或消费者能够通过NameServer查找各topic相应的Broker IP列表。多个NameServer实例组成集群,但相互独立,没有信息交换(无状态,是一个伪集群)。这种特性也就意味着NameServer中任意的结点挂了,只要有一台服务节点正常,整个路由服务就不会有影响。当然,这里不考虑节点的负载情况。

NameServer是一个功能齐全的服务器,角色类似Dubbo中的ZooKeeper,但NameServer与ZooKeeper相比更轻量。主要是因为每个NameServer节点之间是独立的,没有任何信息交互(无状态)。
每个Broker在启动的时候会到NameServer注册,Producer在发送消息之前会根据Topic到NameServer获取到Broker的路由信息,Consumer也会定时获取Topic的路由信息。Broker也会定期向NameServer以发送心跳包的方式,轮询向所有NameServer注册以下元数据信息:

2. RocketMQ部署类型(四种)

单Master:单机模式,即只有一个Broker。

缺点:如果宕机,将导致RocketMQ不可用,不推荐使用;

多Master:组成一个集群,集群每个节点都是Master节点,配置简单,性能也是最高(无同步双写)。

缺点:如果某个节点宕机了, 会导致该节点存在未被消费的消息在节点恢复之前不能被消费。

多Master多Slave模式,异步复制:每个Master配置一个Slave, 多对Master-Slave, Master与Slave消息采用异步复制方式, 主从消息一致只会有毫秒级的延迟。

优点:弥补了多Master模式(无slave)下节点宕机后在恢复前不可订阅的问题。在Master宕机后, 消费者还可以从Slave节点进行消费。采用异步模式复制,提升了一定的吞吐量。总结一句就是,采用多Master多Slave模式,异步复制模式进行部署,系统将会有较低的延迟和较高的吞吐量。
缺点:如果Master宕机, 磁盘损坏的情况下, 如果没有及时将消息复制到Slave, 会导致有少量消息丢失。

多Master多Slave模式,同步双写:与多Master多Slave模式,异步复制方式基本一致,唯一不同的是消息复制采用同步方式,只有master和slave都写成功以后,才会向客户端返回成功。

优点:数据与服务都无单点,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高.
缺点:会降低消息写入的效率,并影响系统的吞吐量

3. RocketMQ消息存储(存储机制)

3.1 何时存储消息

分布式队列因为有高可靠性的要求,所以数据要进行持久化存储。
1、MQ收到一条消息后,需要向生产者返回一个ACK响应,并将消息存储起来。
2、MQ Push一条消息给消费者后,等待消费者的ACK响应,需要将消息标记为已消费。如果没有标记为已消费,MQ会不断的尝试向消费者推送这条消息。
3、MQ需要定期删除一些过期的消息,这样才能保证服务一直可用。

Broker中的消息被消费后不会被立即删除。每条消息都会持久化到CommitLog中,每个Consumer连接到Broker后会维护修改消费进度信息,当有消息消费时只是当前Consumer的消费进度(CommitLog的offset)更新了。

4.6版本默认48小时后会删除不再使用的CommitLog文件。

3.2 消息存储介质

RocketMQ采用的是类似Kafka的文件存储机制,即直接用磁盘来保存消息,而 不需要借助MySQL这一类索引工具。RocketMq采用文件系统进行消息的存储,沿用了Kafka的这两个方面:顺序写和零拷贝。

磁盘如果使用得当,磁盘的速度完全可以匹配上网络 的数据传输速度。目前的高性能磁盘,顺序写速度可以达到600MB/s,超过了一般网卡的传输速度。但是磁盘随机写的速度只有大概100KB/s,和顺序写的性能相差6000倍!因为有如此巨大的速度差别,好的消息队列系统会比普通的消息队列系统速度快多个数量级。RocketMQ的消息用顺序写,保证了消息存储的速度。

零拷贝技术加速文件读写:Linux操作系统分为用户态内核态,文件操作、网络操作需要涉及这两种形态的切换,免不了进行数据复制。一台服务器 把本机磁盘文件的内容发送到客户端,一般分为两个步骤:

  1. read;读取本地文件内容;
  2. 将读取的内容通过网络发送出去。

这两个看似简单的操作,实际上进行了4次数据复制,分别是:

  1. 从磁盘复制数据到内核态内存;
  2. 从内核态内存复 制到用户态内存;
  3. 然后从用户态 内存复制到网络驱动的内核态内存;
  4. 最后是从网络驱动的内核态内存复 制到网卡中进行传输。

而通过使用mmap的方式,可以省去向用户态的内存复制,提高速度。这种机制在Java中是通过NIO包中的MappedByteBuffer实现的。RocketMQ充分利用了上述特性,也就是所谓的“零拷贝”技术,提高消息存盘和网络发送的速度。

这里需要注意的是,采用MappedByteBuffer这种内存映射的方式有几个限制,其中之一是一次只能映射1.5~2G 的文件至用户态的虚拟内存,这也是为何RocketMQ默认设置单个CommitLog日志数据文件为1G的原因了
关于零拷贝,JAVA的NIO中提供了两种实现方式,mmap和sendfile,其中mmap适合比较小的文件,而sendfile适合传递比较大的文件。

3.3 消息存储结构

RocketMQ消息的存储分为三个部分:

  • CommitLog:存储消息的元数据(Topic、QueueId以及message)。所有消息都会顺序存入到CommitLog文件当中。CommitLog由多个文件组成,每个commitLog文件的大小为1G。以第一条消息的偏移量为文件名。
  • CosumerQueue:存储消息在CommitLog的索引。一个MessageQueue一个文件,记录当前MessageQueue被哪些消费者组消费到了哪一条CommitLog。
  • IndexFile:索引文件:存储消息的key和时间戳等信息,为了消息查询提供了一种通过key或时间区间来查询消息的方法,这种通过IndexFile来查找消息的方法不影响发送与消费消息的主流程。

RocketMq将消息均存储在CommitLog中,并分别提供了CosumerQueue和IndexFile两个索引,来快速检索消息。

3.4 刷盘机制

RocketMQ需要将消息存储到磁盘上,这样才能保证断电后消息不会丢失。同时这样才可以让存储的消息量可以超出内存的限制。RocketMQ为了提高性能,会尽量保证磁盘的顺序写。消息在写入磁盘时,有两种写磁盘的方式:同步刷盘异步刷盘

  • 同步刷盘:在返回写成功状态时,消息已经被写入磁盘。具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息写成功的状态。
  • 异步刷盘:在返回写成功状态时,消息可能只是被写入了内存的PAGECACHE,写操作的返回快,吞吐量大;当内存里的消息量累计到一定程度后,统一触发写磁盘操作,快速写入。

配置方式:刷盘方式是通过Broker配置文件里的flushDiskType参数设置的,这个参数被配置成SYNC_FLUSHASYNC_FLUSH中的一个。

3.5 消息主从复制

如果Broker以一个集群的方式部署,会有一个Master节点和多个Slave节点,消息需要从Master复制到Slave上。而消息复制的方式分为同步复制异步复制

  • 同步复制:同步复制是等Master和Slave都写入消息成功后才返回给客户端写入成功的状态。在同步复制下,如果Master节点故障,Slave上有全部的数据备份,这样容易恢复数据。但是同步复制会增大数据写入的延迟,降低系统的吞吐量。
  • 异步复制:异步复制是只要Master写入消息成功,就反馈给客户端写入成功的状态,然后再异步的将消息复制给Slave节点。在异步复制下,系统具有较低的延迟和较高的吞吐量。但是如果Master节点故障,而有些数据没有完成复制,就会造成数据丢失

配置方式:消息复制方式是通过Broker配置文件里的brokerRole参数进行设置的,这个参数可以被设置成ASYNC_MASTERSYNC_MASTERSLAVE三个值中的一个。

4. 防止丢失消息

4.1 Producer防止丢失消息

有三种send方法,同步发送、异步发送、单向发送(OneWay)。发送失败或者超时后可以重试。默认3次(重试次数默认2次)。可以根据api进行更改: producer.setRetryTimesWhenSendFailed(10);

  • 同步发送:发消息的时候会同步阻塞等待broker返回的结果,如果没成功,则不会收到SendResult,这种是最可靠的。但是需要同步等待发送结果,比较耗时。
  • 异步发送:消息发送后立即返回,在回调方法sendCallback里可以得知是否发送成功。通常用于响应时间敏感业务场景。
  • 单向发送:发送端发送完消息后会立即返回,不会等待来自broker的ack来告知本次消息发送是否完全完成发送。这种方式吞吐量很大,但是存在消息丢失的风险,所以其适用于不重要的消息发送,比如日志收集。

4.2 Broker防止丢失消息

在broker端,消息丢失的可能性主要在于刷盘策略同步机制。RocketMQ支持消息的高可靠,影响消息可靠性的几种情况:

  1. Broker非正常关闭
  2. Broker异常Crash
  3. OS Crash
  4. 机器掉电,但是能立即恢复供电情况
  5. 机器无法开机(可能是cpu、主板、内存等关键设备损坏)
  6. 磁盘设备损坏

1、2、3、4 四种情况都属于硬件资源可立即恢复情况,RocketMQ在这四种情况下能保证消息不丢,或者丢失少量数据(依赖刷盘方式是同步还是异步)。

5、6属于单点故障,且无法恢复,一旦发生,在此单点上的消息全部丢失。RocketMQ在这两种情况下,通过异步复制,可保证99%的消息不丢,但是仍然会有极少量的消息可能丢失。通过同步双写技术可以完全避免单点,同步双写势必会影响性能,适合对消息可靠性要求极高的场合,例如与Money相关的应用。注:RocketMQ从3.0版本开始支持同步双写。

Broker端防止丢失消息:

  • 修改刷盘策略为同步刷盘默认情况下是异步刷盘的。当消息投递到broker之后,会先存到page cache,然后根据broker设置的刷盘策略是否立即刷盘,也就是如果刷盘策略为异步,broker并不会等待消息落盘就会返回producer成功,也就是说当broker所在的服务器突然宕机,则会丢失部分页的消息。
## 默认情况为 ASYNC_FLUSH,修改为同步刷盘:SYNC_FLUSH,实际场景看业务,同步刷盘效率肯定不如异步刷盘高。
flushDiskType = SYNC_FLUSH 
  • 集群部署,提供主从模式,同时主从同步双写。即使broker设置了同步刷盘,如果主broker磁盘损坏,也是会导致消息丢失。 因此可以给broker指定slave,同时设置master为SYNC_MASTER,然后将slave设置为同步刷盘策略。此模式下,producer每发送一条消息,都会等消息投递到master和slave都落盘成功了,broker才会当作消息投递成功,保证休息不丢失。
## 默认为 ASYNC_MASTER
brokerRole=SYNC_MASTER

4.3 Consumer防止丢失消息

  • 消息异常重试。注:消息异常重试时易发生吞没消息现象。
  • 注意返回消息状态。只有当业务逻辑真正执行成功,我们才能返回 ConsumeConcurrentlyStatus.CONSUME_SUCCESS。否则我们需要返回ConsumeConcurrentlyStatus.RECONSUME_LATER,稍后再重试。

5. 负载均衡

5.1 Producer负载均衡

Producer发送消息时,默认会轮询目标Topic下的所有MessageQueue,并采用递增取模的方式往不同的MessageQueue上发送消息,以达到让消息平均落在不同的queue上的目的。而由于MessageQueue是分布在不同的Broker上的,所以消息也会发送大不同的Broker上。

同时生产者在发送消息时,可以自定义实现MessageQueueSelector。通过这个对象来讲消息发送到自己指定的MessageQueue上。这样可以保证消息局部有序。

5.2 Consumer负载均衡

Consumer也是以MessageQueue为单位来进行负载均衡。分为集群模式和广播模式。

1、集群模式

在集群消费模式下,每条消息只需要投递到订阅这个topic的Consumer Group下的一个实例即可。RocketMQ采用主动拉取的方式拉取并消费消息,在拉取的时候需要明确指定拉取哪一条messagequeue。

而每当实例的数量有变更,都会触发一次所有实例的负载均衡,这时候会按照queue的数量和实例的数量平均分配queue给每个实例。

每次分配时,都会将MessageQueue和消费者ID进行排序后,再用不同的分配算法进行分配。内置的分配的算法共有六种,分别对应AllocateMessageQueueStrategy下的六种实现类,可以在consumer中直接set来指定。默认情况下使用的是最简单的平均分配策略

AllocateMachineRoomNearby: 将同机房的Consumer和Broker优先分配在一起。

这个策略可以通过一个machineRoomResolver对象来定制Consumer和Broker的机房解析规则。然后还需要引入另外一个分配策略来对同机房的Broker和Consumer进行分配。一般也就用简单的平均分配策略或者轮询分配策略。

2、广播模式

广播模式下,每一条消息都会投递给订阅了Topic的所有消费者实例,所以也就没有消息分配这一说。而在实现上,就是在Consumer分配Queue时,所有Consumer都分到所有的Queue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值