消息中间件之RocketMQ相关知识

目录

收起

一、RocketMQ架构

RocketMQ使用场景

RocketMQ 部署架构

RocketMQ特性

消费模式Push or Pull

RocketMQ核心概念

RocketMQ环境搭建

二、RocketMQ特性

消息发送机制

消息消费机制

消息存储机制

消息过滤机制

零拷贝原理

同步复制和异步复制

高可用机制

刷盘机制

负载均衡

消息重试

死信队列

延迟消息

顺序消息

事务消息

消息查询及优先级

底层网络通信 – Netty

限流机制

三、RocketMQ高级实战

生产者

消费者

Broker

NameServer

系统配置

RocketMQ集群

前言

本篇文章包含RocketMQ使用场景、消费模式、高可用机制、负载均衡、延迟消息等等,希望对大家有所帮助。

一、RocketMQ架构

RocketMQ使用场景

1. 应用解耦:系统的耦合性越高,容错性就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、 物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。

2. 流量削峰:缓存起来,分散到很长一段时间处理,这样可以大大提到系统的稳定性和用户体验。

3. 数据分发:通过消息队列可以让数据在多个系统之间进行流通。数据的产生方不需要关心谁来使用数据,只需 要将数据发送到消息队列,数据使用方直接在消息队列中直接获取数据即可

RocketMQ 部署架构

RocketMQ的角色:

Producer:消息的发送者;举例:发信者

Consumer:消息接收者;举例:收信者

Broker:暂存和传输消息;举例:邮局

NameServer:管理Broker;举例:各个邮局的管理机构

Topic:区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者 可以订阅一个或者多个Topic消息

Message Queue:相当于是Topic的分区;用于并行发送和接收消息

执行流程:

1. 启动NameServer,NameServer起来后监听端口,等待Broker、Producer、Consumer连上来,相当于一个路由控制中心。

2. Broker启动,跟所有的NameServer保持长连接,定时发送心跳包。心跳包中包含当前 Broker信息(IP+端口等)以及存储所有Topic信息。注册成功后,NameServer集群中就有Topic 跟Broker的映射关系。

3. 收发消息前,先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic。

4. Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,并从 NameServer中获取当前发送的Topic存在哪些Broker上,轮询从队列列表中选择一个队列, 然后与队列所在的Broker建立长连接从而向Broker发消息。

5. Consumer跟Producer类似,跟其中一台NameServer建立长连接,获取当前订阅Topic存在 哪些Broker上,然后直接跟Broker建立连接通道,开始消费消息。

RocketMQ特性

1. 订阅与发布:消息的发布是指某个生产者向某个topic发送消息;消息的订阅是指某个消费者关注了某个topic中带有某些tag的消息。

2. 消息顺序:消息有序指的是一类消息消费时,能按照发送的顺序来消费。RocketMQ可以严格的保证消息有序。

3. 消息过滤:RocketMQ的消费者可以根据Tag进行消息过滤,也支持自定义属性过滤。

4. 消息可靠性:RocketMQ支持消息的高可靠,影响消息可靠性的几种情况: 1)Broker非正常关闭 2)Broker异常 Crash 3)OS Crash 4)机器掉电,但是能立即恢复供电情况 5)机器无法开机(可能是cpu、主板、内存等 关键设备损坏) 6)磁盘设备损坏,RocketMQ通过异步复制,可保证99%的消息不丢,但是仍然会有极少量的消息可能丢失。通过同步双写技术可以完全避免单点,但是性能会下降。

5. 至少一次:指每个消息必须投递一次。Consumer先Pull消息到本地,消费完成后,才向服务器返回ack,如果没有消费一定不会ack消息,所以RocketMQ可以很好的支持此特性。

6. 回溯消费:回溯消费是指Consumer已经消费成功的消息,由于业务上需求需要重新消费,要支持此功能, Broker在向Consumer投递成功消息后,消息仍然需要保留。

7. 事务消息:RocketMQ事务消息是指应用本地事务和发送消息操作可以被定义到全局事务中,要么同时成功,要么同时失败。

8. 定时消息:定时消息(延迟队列)是指消息发送到broker后,不会立即被消费,等待特定时间投递给真正的 topic。

9. 消息重试:Consumer消费消息失败后,要提供一种重试机制,令消息再消费一次。

10. 消息重投:消息重投保证消息尽可能发送成功、不丢失,但可能会造成消息重复,消息重复在RocketMQ中是 无法避免的问题。

11. 流量控制:生产者流控,因为broker处理能力达到瓶颈,不会尝试消息重投;消费者流控,因为消费能力达到瓶颈。

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

消费模式Push or Pull

RocketMQ消息订阅有两种模式,一种是Push模式,即MQServer主动向消费端推送;另外一种是Pull模式,即消费端在需要时,主动到MQ Server拉取。但在具体实现时,Push和Pull模式本质都是采用消费端主动拉取的方式,即consumer轮询从 broker拉取消息。RocketMQ使用长轮询机制来模拟Push效果,算是兼顾了二者的优点。

Push模式

实时性高,但是消费端的处理能力有限,当瞬间推送很多消息给消费端时,容易造成消费端的消息积压,严重时会压垮客户端,Push方式里,consumer把长轮询的动作封装了,并注册MessageListener监听器,取到消息后,唤醒MessageListener的consumeMessage()来消费,对用户而言,感觉消息是被推送过来的。

Pull模式

主动权掌握在消费端自己手中,根据自己的处理能力量力而行。但是Pull的频率,定时间隔太久担心影响时效性,间隔太短担心做太多“无用功”浪费资源。比较折中的办法就是长轮询。Pull方式里,取消息的过程需要用户自己主动调用,首先通过打算消费的Topic拿到 MessageQueue的集合,遍历MessageQueue集合,然后针对每个MessageQueue批量取消息,一次取完后,记录该队列下一次要取的开始offset,直到取完了,再换另一个MessageQueue。

RocketMQ核心概念

1. 消息模型:RocketMQ主要由Producer、Broker、Consumer 三部分组成,其中Producer 负责生产消息, Consumer 负责消费消息,Broker 负责存储消息。

2. Producer:消息生产者,负责产生消息,一般由业务系统负责产生消息。

3. Consumer:消息消费者,负责消费消息,一般是后台系统负责异步消费。

4. PushConsumer:Consumer消费的一种类型,该模式下Broker收到数据后会主动推送给消费端。应用通常向 Consumer对象注册一个Listener接口,一旦收到消息,Consumer对象立刻回调Listener接口方法。该 消费模式一般实时性较高。

5. PullConsumer:Consumer消费的一种类型,应用通常主动调用Consumer的拉消息方法从Broker服务器拉消息、 主动权由应用控制。一旦获取了批量消息,应用就会启动消费过程。

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

7. ConsumerGroup:同一类Consumer的集合,这类Consumer通常消费同一类消息且消费逻辑一致。消费者组使得在 消息消费方面,实现负载均衡和容错的目标变得非常容易。要注意的是,消费者组的消费者实例必须订 阅完全相同的Topic。RocketMQ 支持两种消息模式:集群消费和广播消费。

8. Broker:消息中转角色,负责存储消息,转发消息,一般也称为 Server。

9. 一条消息被多个 Consumer 消费,即使这些 Consumer 属于同一个 Consumer Group,消息也会被 Consumer Group 中的每个 Consumer 都消费一次,广播消费中的 Consumer Group 概念可以认为在消息划分方面无意义。

10. 集群消费:一个 Consumer Group 中的 Consumer 实例平均分摊消费消息。例如某个 Topic 有 9 条消息,其 中一个 Consumer Group 有 3 个实例,那举每个实例只消费其中的 3 条消息。

11. 顺序消息:消费消息的顺序要同发送消息的顺序一致,在RocketMQ 中主要指的是局部顺序,即一类消息为满足顺序性,必须Producer单线程顺序发送,且发送到同一个队列,这样Consumer 就可以按照 Producer发送的顺序去消费消息

12. 普通顺序消息:顺序消息的一种,正常情况下可以保证完全的顺序消息,但是一旦发生通信异常,Broker 重启, 由于队列总数发生发化,哈希取模后定位的队列会发化,产生短暂的消息顺序不一致。

13. 严格顺序消息:顺序消息的一种,无论正常异常情况都能保证顺序,但是牺牲了分布式 Failover特性,即Broker集 群中只要有一台机器不可用,则整个集群都不可用,服务可用性大大降低。

14. Message Queue:在 RocketMQ 中,所有消息队列都是持久化的,长度无限的数据结构,所谓长度无限是指队列中 的每个存储单元都是定长,访问其中的存储单元使用Offset来访问,offset 为 java long 类型,64 位, 理论上在 100 年内不会溢出,所以认为为是长度无限,另外队列中只保存最近几天的数据,之前的数据会按照过期时间来删除。

15. 标签(Tag):为消息设置的标志,用于同一主题下区分不同类型的消息。来自同一业务单元的消息,可以根据不 同业务目的在同一主题下设置不同标签。

RocketMQ环境搭建

第一步:安装unzip,解压zip

yum install -y unzip zip

第二步:下载rocket包并解压

wget https://archive.apache.org/dist/rocketmq/4.5.1/rocketmq-all4.5.1-bin-release.zip

unzip unzip rocketmq-all-4.5.1-bin-release.zip

第三步:环境变量配置,配套jdk8以上

vim /etc/profile #修改配置

export ROCKET_HOME=/usr/local/rocketmq/rocket

export PATH=$PATH:$ROCKET_HOME/bin

source /etc/profile #生效

第四步:修改启动、关闭配置文件,更改占用内存 64m 128m

vim bin/runserver.sh

vim bin/runbroker.sh

vim conf/broker.conf

第五步:启动NameServer

sh bin/mqnamesrv -n 117.50.5.252:9876 &

第六步:启动Broker

sh bin/mqbroker -n 117.50.5.252:9876 autoCreateTopicEnable=true -c /usr/local/rocketmq/rocket/conf/broker.conf &

第七步:停止命令

mqshutdown borker

mqshutdown namesrv

二、RocketMQ特性

消息发送机制

生产者向消息队列里写入消息,不同的业务场景需要生产者采用不同的写入策略。比如同步发送、异步发送、OneWay发送、延迟发送、发送事务消息等。 默认使用的是DefaultMQProducer类,发送消息要经过五个步骤:

1)设置Producer的GroupName。

2)设置InstanceName,当一个Jvm需要启动多个Producer的时候,通过设置不同的 InstanceName来区分,不设置的话系统使用默认名称“DEFAULT”。

3)设置发送失败重试次数,当网络出现异常的时候,这个次数影响消息的重复投递次数。想保证不丢消息,可以设置多重试几次。

4)设置NameServer地址

5)组装消息并发送。

提升写入的性能 发送一条消息出去要经过三步:

1. 客户端发送请求到服务器。 2. 服务器处理该请求。 3. 服务器向客户端返回应答

Oneway方式只发送请求不等待应答,即将数据写入客户端的Socket缓冲区就返回,不等待对方返回结果。

另一种提高发送速度的方法是增加Producer的并发量,使用多个Producer同时发送,RocketMQ引入了一个并发窗口,在窗口内消息可以并发地写入DirectMem中,然后异步地将连续一段无空洞的数据刷入文件系统当中,写入性能达到90万+的TPS。

消息消费机制

消费的几个要点:

1. 消息消费方式(Pull和Push)

2. 消息消费的模式(广播模式和集群模式)

3. 流量控制(可以结合sentinel来实现)

4. 并发线程数设置

5. 消息的过滤(Tag、Key) TagA||TagB||TagC * null

三种提高Consumer的处理能力的方法:

1. 提高消费并行度,在同一个ConsumerGroup下(Clustering方式),可以通过增加Consumer实例的数量来提 高并行度。 通过加机器,或者在已有机器中启动多个Consumer进程都可以增加Consumer实例数。

2. 以批量方式进行消费,某些业务场景下,多条消息同时处理的时间会大大小于逐个处理的时间总和,比如消费消息中,涉及update某个数据库,一次update10条的时间会大大小于十次update1条数据的时间。

3. 检测延时情况,跳过非重要消息,Consumer在消费的过程中,如果发现由于某种原因发生严重的消息堆积,短时间无法消除堆 积,这个时候可以选择丢弃不重要的消息,使Consumer尽快追上Producer的进度。

消息存储机制

消息存储

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

存储结构

RocketMQ消息的存储是由ConsumeQueue和CommitLog配合完成 的,消息真正的物理存储文件 是CommitLog,ConsumeQueue是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储 的地址。每 个Topic下的每个Message Queue都有一个对应的ConsumeQueue文件。

1) CommitLog:消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消 息内容不是定长的。

2) ConsumeQueue:消息消费队列,引入的目的主要是提高消息消费的性能。

3) IndexFile:IndexFile(索引文件)提供了一种可以通过key或时间区间来查询消息的方法。

消息过滤机制

RocketMQ分布式消息队列的消息过滤方式有别于其它MQ中间件,是在Consumer端订阅消息时再做消息过滤的。 RocketMQ这么做是在于其Producer端写入消息和Consumer端订阅消息采用分离存储的机制来实现的,Consumer端订阅消息是需要通过ConsumeQueue这个消息消费的逻辑队列拿到一个索引,然后再从CommitLog里面读取真正的消息实体内容。

Tag过滤方式:

Consumer端在订阅消息时除了指定Topic还可以指定TAG,如果一个消息有多 个TAG,可以用||分隔。

1. Consumer端会将这个订阅请求构建成一个 SubscriptionData,发送一个Pull消息的请求给 Broker端。

2. Broker端从RocketMQ的文件存储层—Store读取数据之前,会用这些数据先构建一个 MessageFilter,然后传给Store。

3. Store从 ConsumeQueue读取到一条记录后,会用它记录的消息tag hash值去做过滤。

4. 在服务端只是根据hashcode进行判断,无法精确对tag原始字符串进行过滤,在消息消费端拉 取到消息后,还需要对消息的原始tag字符串进行比对,如果不同,则丢弃该消息,不进行消 息消费。

SQL92的过滤方式:

仅对push的消费者起作用。 Tag方式虽然效率高,但是支持的过滤逻辑比较简单。 SQL表达式可以更加灵活的支持复杂过滤逻辑。

1. 数字比较: >, >=, <=, BETWEEN, =

2. 字符串比较: =, <>, IN; IS NULL或者IS NOT NULL;

3. 逻辑比较: AND, OR, NOT;

4. Constant types are: 数字如:123, 3.1415; 字符串如:'abc',必须是单引号引起来 NULL,特 殊常量 布尔型如:TRUE or FALSE;

零拷贝原理

cache和buffer的区别

Cache:缓存区,是高速缓存,是位于CPU和主内存之间的容量较小但速度很快的存储器,因 为CPU的速度远远高于主内存的速度,CPU从内存中读取数据需等待很长的时间,而 Cache 保存着CPU刚用过的数据或循环使用的部分数据,这时从Cache中读取数据会更快,减少了 CPU等待的时间,提高了系统的性能。

Buffer:缓冲区,用于存储速度不同步的设备或优先级不同的设备之间传输数据;通过buffer 可以减少进程间通信需要等待的时间,当存储速度快的设备与存储速度慢的设备进行通信时, 存储慢的数据先把数据存放到buffer,达到一定程度存储快的设备再读取buffer的数据,在此 期间存储快的设备CPU可以干其他的事情。

HeapByteBuffer和DirectByteBuffer

HeapByteBuffer,是在jvm堆上面一个buffer,底层的本质是一个数组,用类封装维护了很多的 索引(limit/position/capacity等)。

DirectByteBuffer,底层的数据是维护在操作系统的内存中,而不是jvm里,DirectByteBuffer里维 护了一个引用address指向数据,进而操作数据。

HeapByteBuffer优点:内容维护在jvm里,把内容写进buffer里速度快;更容易回收。

DirectByteBuffer优点:跟外设(IO设备)打交道时会快很多,因为外设读取jvm堆里的数据时, 不是直接读取的,而是把jvm里的数据读到一个内存块里,再在这个块里读取的,如果使用 DirectByteBuffer,则可以省去这一步,实现zero copy(零拷贝)

缓冲IO和直接IO

缓存I/O又被称作标准I/O,大多数文件系统的默认I/O操作都是缓存I/O。

1. 在一定程度上分离了内核空间和用户空间,保护系统本身的运行安全;

2. 可以减少读盘的次数,从而提高性能。

缓存I/O数据在传输过程中就需要在应用程序地址空间(用户空间)和缓存(内核空间)之间进行多次数据拷贝操作, 这些数据拷贝操作所带来的CPU以及内存开销是非常大的。

直接IO就是应用程序直接访问磁盘数据,而不经过内核缓冲区,这样做的目的是减少一次从内核缓 冲区到用户程序缓存的数据复制。

如果访问的数据不在应用程序缓存中,那么每次数据都会直接从磁盘加载,这种直接加载会非常缓慢。通常直接IO与异步IO结合使用,会得到比较好的性能。

总结

1. 虽然叫零拷贝,实际上sendfile有2次数据拷贝的。第1次是从磁盘拷贝到内核缓冲区,第二次是从内核缓冲区拷贝到网卡(协议引擎)。如果网卡支持 SG-DMA技术,就无需从PageCache拷贝至 Socket 缓冲区;

2. 之所以叫零拷贝,是从内存角度来看的,数据在内存中没有发生过拷贝,只是在内存和I/O设备之间传输。很多时候我们认为sendfile才是零拷贝,mmap严格来说不算;

3. Linux中的API为sendfile、mmap,Java中的API为FileChanel.transferTo()、 FileChannel.map()等;

4. Netty、Kafka(sendfile)、Rocketmq(mmap)、Nginx等高性能中间件中,都有大量利用操作系统零拷贝特性。

同步复制和异步复制

如果一个Broker组有Master和Slave,消息需要从Master复制到Slave 上,有同步和异步两种复制方式。

同步复制:

同步复制方式是等Master和Slave均写 成功后才反馈给客户端写成功状态; 在同步复制方式下,如果Master出故障,Slave上有全部的备份数据,容易恢复,但是同步复制会 增大数据写入延迟,降低系统吞吐量。

异步复制:

异步复制方式是只要Master写成功 即可反馈给客户端写成功状态。 在异步复制方式下,系统拥有较低的延迟和较高的吞吐量,但是如果Master出了故障,有些数据因 为没有被写 入Slave,有可能会丢失;

同步复制和异步复制是通过broker.conf 配置文件里的brokerRole参数进行设置的,这个参数可以被设置成ASYNC_MASTER、 SYNC_MASTER、SLAVE三个值中的一个。

通常情况下,应该把Master和Save配置成ASYNC_FLUSH的 刷盘 方式,主从之间配置成SYNC_MASTER的复制方式,这样即使有一台机器出故障,仍然能保证数据不丢。

高可用机制

RocketMQ分布式集群是通过Master和Slave的配合达到高可用性的。

消息消费高可用:在Consumer的配置文件中,并不需要设置是从Master读还是从Slave 读,当Master不可用或者繁忙的时候,Consumer会被自动切换到从Slave 读。

消息发送高可用:在创建Topic的时候,把Topic的多个Message Queue创建在多个Broker组上,这样既可以在性能方面具有扩展性,也可以降低主节点故障 对整体上带来的影响,而且当一个Broker组的Master不可用后,其他组的Master仍然可用,Producer 仍然可以发送消息的。

在需要保证消息严格顺序的场景下,由于在主题层面无法保证严格顺序,所以必须指定队列来发送消息,对于任何一个队列,它一定是落在一组特定的主从节点上,如果这个主节点宕机,其他的主节点是无法替代这个主节点的,否则就无法保证严格顺序。 在这种复制模式下,严格顺序和高可用只能选择一个。RocketMQ 在 2018 年底迎来了一次重大的更新,引入 Dledger,增加了一种全新的复制方式解决了这个问题。

刷盘机制

RocketMQ 的所有消息都是持久化的,先写入系统 PageCache,然后刷盘,可以保证内存与磁盘 都有一份数据, 访问时,直接从内存读取。消息在通过Producer写入RocketMQ的时候,有两种写磁盘方式,分布式同步刷盘和异步刷盘。

同步刷盘和异步刷盘差异:

同步刷盘与异步刷盘的唯一区别是异步刷盘写完 PageCache直接返回,而同步刷盘需要等待刷盘完成才返回, 同步刷盘流程如下: (1). 写入 PageCache后,线程等待,通知刷盘线程刷盘。 (2). 刷盘线程刷盘后,唤醒前端等待线程,可能是一批线程。 (3). 前端等待线程向用户返回成功

负载均衡

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

Producer的负载均衡:

Consumer的负载均衡:

在RocketMQ中,负载均衡或者消息分配是在Consumer端代码中完成的,Consumer从Broker处 获得全局信息,然后自己做负载均衡,只处理分给自己的那部分消息。 Pull Consumer可以看到所有的Message Queue,而且从哪个Message Queue读取消息,读消息 时的Offset都由使用者控制,使用者可以实现任何特殊方式的负载均衡。 DefaultMQPullConsumer有两个辅助方法可以帮助实现负载均衡,一个是 registerMessageQueueListener函数,一个是MQPullConsumerScheduleService。

DefaultMQPushConsumer的负载均衡过程不需要使用者操心,客户端程序会自动处理,每个 DefaultMQPushConsumer启动后,会马上会触发一个doRebalance动作;而且在同一个 ConsumerGroup里加入新的DefaultMQPush-Consumer时,各个Consumer都会被触发 doRebalance动作。

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

消息重试

顺序消息的重试

对于顺序消息,当消费者消费消息失败后,消息队列 RocketMQ 会自动不断进行消息重试(每次间隔时间为 1 秒),这时,应用会出现消息消费被阻塞的情况。因此,在使用顺序消息时,务必保证应 用能够及时监控并处理消费失败的情况,避免阻塞现象的发生。

无序消息的重试

对于无序消息(普通、定时、延时、事务消息),当消费者消费消息失败时,您可以通过设置返回 状态达到消息重试的结果。无序消息的重试只针对集群消费方式生效;广播方式不提供失败重试特性,即消费失败后,失败消 息不再重试,继续消费新的消息。

消息队列 RocketMQ 默认允许每条消息最多重试 16 次,每次重试的间隔时间如下:

如果消息重试 16 次后仍然失败,消息将不再投递。

注意:

1) 消息最大重试次数的设置对相同 Group ID 下的所有 Consumer 实例有效。

2) 如果只对相同 Group ID 下两个 Consumer 实例中的其中一个设置了 MaxReconsumeTimes,那么该配置对两个 Consumer 实例均生效。

3) 配置采用覆盖的方式生效,即最后启动的 Consumer 实例会覆盖之前的启动实例的配置

死信队列

RocketMQ中消息重试超过一定次数后(默认16次)就会被放到死信队列中,在消息队列 RocketMQ 中,这种正常情况下无法被消费的消息称为死信消息(Dead-Letter Message),存储死信 消息的特殊队列称为死信队列(Dead-Letter Queue)。

可视化工具:rocketmq-console下载地址:

https://github.com/apache/rocketmq-externals/archive/rocketmq-console-1.0.0.zip

死信消息特性:

1) 不会再被消费者正常消费。

2) 有效期与正常消息相同,均为 3 天,3 天后会被自动删除。因此,请在死信消息产生后的 3 天内及时处理。

死信队列特征:

1) 一个死信队列对应一个 Group ID, 而不是对应单个消费者实例。

2) 如果一个 Group ID 未产生死信消息,消息队列 RocketMQ 不会为其创建相应的死信队列。

3) 一个死信队列包含了对应 Group ID 产生的所有死信消息,不论该消息属于哪个 Topic。

一条消息进入死信队列,意味着某些因素导致消费者无法正常消费该消息,因此,通常需要您对其进行特殊处理。排查可疑因素并解决问题后,可以在消息队列 RocketMQ 控制台重新发送该消息,让消费者重新消费一次。

延迟消息

定时消息(延迟队列)是指消息发送到broker后,不会立即被消费,等待特定时间投递给真正的 topic。 broker有配置项messageDelayLevel,默认值为“1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”,18个level。可以配置自定义messageDelayLevel。

level有以下三种情况:

level == 0,消息为非延迟消息

1<=level<=maxLevel,消息延迟特定时间,例如level==1,延迟1s

level > maxLevel,则level== maxLevel,例如level==20,延迟2h

发消息时,设置delayLevel等级即可: msg.setDelayLevel(level)。

顺序消息

顺序消息是指消息的消费顺序和产生顺序相同,在有些业务逻辑下,必须保证顺序。比如订单的生 成、付款、发货,这3个消息必须按顺序处理才行。

顺序消息分为全局顺序消息和部分顺序消息:

1. 全局顺序消息指某个Topic下的所有消息都要保证顺序;

2. 部分顺序消息只要保证每一组消息被顺序消费即可,比如上面订单消息的例子,只要保证同一个订单ID的三个消息能按顺序消费即可。

要保证部分消息有序,需要发送端和消费端配合处理。在发送端,要做到把同一业务ID的消息发送 到同一个Message Queue;在消费过程中,要做到从同一个Message Queue读取的消息不被并发处理,这样才能达到部分有序。消费端通过使用MessageListenerOrderly类来解决单Message Queue的消息被并发处理的问题。

要保证全局顺序消息,需要先把Topic的读写队列数设置为一,然后Producer和Consumer的并发设置也要是一。简单来说,为了保证整个Topic的全局消息有序,只能消除所有的并发处理,各部分都设置成单线程处理。

事务消息

RocketMQ的事务消息,是指发送消息事件和其他事件需要同时成功或同时失败。比如银行转账, A银行的某账户要转一万元到B银行的某账户。A银行发送“B银行账户增加一万元”这个消息,要和“从A银 行账户扣除一万元”这个操作同时成功或者同时失败。RocketMQ采用两阶段提交的方式实现事务消息。

具体流程如下:

1)发送方向RocketMQ发送“待确认”消息。

2)RocketMQ将收到的“待确认”消息持久化成功后,向发送方回复消息已经发送成功,此时第一阶段消息发送完成。

3)发送方开始执行本地事件逻辑。

4)发送方根据本地事件执行结果向RocketMQ发送二次确认(Commit或是Rollback)消息, RocketMQ收到Commit状态则将第一阶段消息标记为可投递,订阅方将能够收到该消息;收到 Rollback状态则删除第一阶段的消息,订阅方接收不到该消息。

5)如果出现异常情况,步骤4)提交的二次确认最终未到达RocketMQ,服务器在经过固定时间段 后将对“待确认”消息发起回查请求。

6)发送方收到消息回查请求后(如果发送一阶段消息的Producer不能工作,回查请求将被发送到 和Producer在同一个Group里的其他Producer),通过检查对应消息的本地事件执行结果返回Commit 或Roolback状态。

7)RocketMQ收到回查请求后,按照步骤4)的逻辑处理。

RocketMQ事务消息流程概要

事务消息发送及提交:(1) 发送消息(half消息)。 (2) 服务端响应消息写入结果。 (3) 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)。 (4) 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可 见)

事务消息的补偿流程:(1) 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查” (2) Producer收到回查消息,检查回查消息对应的本地事务的状态 (3) 根据本地事务状态,重新Commit或者Rollback

补偿阶段用于解决消息Commit或者Rollback发生超时或者失败的情况。

RocketMQ事务消息设计

1. 事务消息在一阶段对用户不可见

2. Commit和Rollback操作以及Op消息的引入

3. Op消息的存储和对应关系,Op消息的内容为对应的Half消息的存储的Offset,这样通过Op消息能索引到 Half消息进行后续的回查操作。

4. Half消息的索引构建,在执行二阶段Commit操作时,需要构建出Half消息的索引。

5. 处理二阶段失败的消息,如果在RocketMQ事务消息的二阶段过程中失败了,例如在做Commit操作时,出现网络问题导致 Commit失败,那么需要通过一定的策略使这条消息最终被Commit。RocketMQ采用了一种补偿机制, 称为“回查”。

消息查询及优先级

消息查询

RocketMQ支持按照下面两种维度(“按照Message Id查询消息”、“按照Message Key查询消息”)进行消息查询。

按照MessageId查询消息:MsgId 总共 16 字节,包含消息存储主机地址(ip/port),消息 Commit Log offset。

按照Message Key查询消息:主要是基于RocketMQ的IndexFile索引文件来实现的。

消息优先级

有些场景,需要应用程序处理几种类型的消息,不同消息的优先级不同。RocketMQ是个先入先出的队列,不支持消息级别或者Topic级别的优先级。

1) 多个不同的消息类型使用同一个topic时,由于某一个种消息流量非常大,导致其他类型的消息无法及时消费,造成不公平,所以把流量大的类型消息在一个单独的 Topic,其他类型消息在另外一个 Topic,应用程序创建两个 Consumer,分别订阅不同的 Topic。创建一个 Topic, 设置Topic的 MessageQueue 数量超过 100 个,Producer根据订 单的门店号,把每个门店的订单写人 一 个 MessageQueue。 DefaultMQPushConsumer默认是采用 循环的方式逐个读取一个 Topic 的所有 MessageQueue,这样如果某家门店订单量大增,这家门店对 应的 MessageQueue 消息数增多,等待时间增长,但不会造成其他家门店等待时间增长。

2) 情况和第一种情况类似,但是不用创建大量的Topic。

3) 强制优先级 TypeA、 TypeB、 TypeC 三类消息 。TypeA 处于第一优先级,要确保只要有TypeA消息,必须优先处理; TypeB处于第二优先 级; TypeC 处于第三优先级 。

底层网络通信 – Netty

RocketMQ底层通信的实现是在Remoting模块里,因为借助了Netty而没有重复造轮子, RocketMQ的通信部分没有很多的代码,就是用Netty实现了一个自定义协议的客户端/服务器程序。

1. 自定义ByteBuf可以从底层解决ByteBuffer的一些问题,并且通过“内存池”的设计来提升性能

2. Reactor主从多线程模型

3. 充分利用了零拷贝,CAS/volatite高效并发编程特性

4. 无锁串行化设计

5. 管道责任链的编程模型

6. 高性能序列化框架的支持

7. 灵活配置TCP协议参数

RocketMQ消息队列中支持通信的方式主要有同步(sync)、异步(async)、单向(oneway) 三种。

RocketMQ的RPC通信采用Netty组件作为底层通信库,同样也遵循了Reactor多线程模型,同时又在这之上做了一些扩展和优化。

限流机制

RocketMQ消费端中我们可以:

1. 设置最大消费线程数 2. 每次拉取消息条数等

同时:

1. PushConsumer会判断获取但还未处理的消息个数、消息总大小、Offset的跨度, 2. 任何一个值超过设定的大小就隔一段时间再拉取消息,从而达到流量控制的目的。

Sentinel 专门为这种场景提供了匀速器的特性,可以把突然到来的大量请求以匀速的形式均摊,以 固定的间隔时间让请求通过,以稳定的速度逐步处理这些请求,起到“削峰填谷”的效果,从而避免流量突刺造成系统负载过高。同时堆积的请求将会排队,逐步进行处理;当请求排队预计超过最大超时时长 的时候则直接拒绝,而不是拒绝全部请求。比如在 RocketMQ 的场景下配置了匀速模式下请求 QPS 为 5,则会每 200 ms 处理一条消息,多 余的处理任务将排队;同时设置了超时时间为 5 s,预计排队时长超过 5s 的处理任务将会直接被拒绝。

三、RocketMQ高级实战

生产者

Tags的使用:一个应用尽可能用一个Topic,而消息子类型则可以用tags来标识。tags可以由应用自由设置,只 有生产者在发送消息设置了tags,消费方在订阅消息时才可以利用tags通过broker做消息过滤: message.setTags("TagA")。

Keys的使用:每个消息在业务层面的唯一标识码要设置到keys字段,方便将来定位消息丢失问题。服务器会为每 个消息创建索引(哈希索引),应用可以通过topic、key来查询这条消息内容,以及消息被谁消费。由 于是哈希索引,请务必保证key尽可能唯一,这样可以避免潜在的哈希冲突。

日志的打印:

1) SEND_OK:消息发送成功。

2) FLUSH_DISK_TIMEOUT:消息发送成功但是服务器刷盘超时。

3) FLUSH_SLAVE_TIMEOUT:消息发送成功,但是服务器同步到Slave时超时。

4) SLAVE_NOT_AVAILABLE:消息发送成功,但是此时Slave不可用。

消息发送失败处理方式:Producer的send方法本身支持内部重试,至多重试2次,如果发送失败,则轮转到下一个Broker,如果本身向broker发送消息产生超时异常,就不会再重试。

选择oneway形式发送:oneway形式只发送请求 不等待应答,而发送请求在客户端实现层面仅仅是一个操作系统系统调用的开销,即将数据写入客户端 的socket缓冲区,此过程耗时通常在微秒级。

消费者

消费过程幂等:RocketMQ无法避免消息重复,所以如果业务对消费重复非常敏感,务必要在业务层面进行去重处理,可以借助关系数据库进行去重。

消费速度慢的处理方:1. 提高消费并行度,2. 批量方式消费,3. 跳过非重要消息。

优化每条消息消费过程:把循环多次处理变为批量单次处理,减少IO次数。

消费打印日志:在消费入口方法打印消息,消费耗时等,方便后续排查问题。

其他消费建议:1. 确保同一组内的每个消费者订阅信息保持一致。2.使用有序消息,消费者将锁定每个消息队列,以确保他们被逐个消费。3. 并发消费不建议抛出异常,直接返回状态码。4. 不建议阻塞监听器,因为它会阻塞线程池,并最终可能会终止消费进程。

Broker

Broker 角色分为 ASYNC_MASTER(异步主机)、SYNC_MASTER(同步主机)以及SLAVE(从 机)。SYNC_FLUSH(同步刷新)相比于ASYNC_FLUSH(异步处理)会损失很多性能,但是也更可靠, 所以需要根据实际的业务场景做好权衡。

NameServer

NameServer的设计:

1. NameServer互相独立,彼此没有通信关系,单台NameServer挂掉,不影响其他 NameServer。

2. NameServer不去连接别的机器,不主动推消息。

3. 单个Broker(Master、Slave)与所有NameServer进行定时注册,以便告知NameServer自 己还活着。

4. Consumer随机与一个NameServer建立长连接,如果该NameServer断开,则从 NameServer列表中查找下一个进行连接。

5. Producer随机与一个NameServer建立长连接,每隔30秒(此处时间可配置)从 NameServer获取Topic的最新队列情况,如果某个Broker Master宕机,Producer最多30秒 才能感知,在这个期间,发往该broker master的消息失败。Producer向提供Topic服务的 Master建立长连接,且定时向Master发送心跳。

RocketMQ为什么不使用ZooKeeper而自己开发NameServer?

zookeeper在粗粒度分布式锁,分布式选主,主备高可用切换等不需要高TPS支持的场景下有不可替代的作用,而这些需求往往多集中在大数据、离线任务等相关的业务领域,因为大数据领域,讲究分割数据集,并且大部分时间分任务多进程/线程并行处理这些数据集,但是总是有一些点上需要将这些任务和进程统一协调,这时候就是ZooKeeper发挥巨大作用的用武之地。 但是在交易场景交易链路上,在主业务数据存取,大规模服务发现、大规模健康监测等方面有天然 的短板,应该竭力避免在这些场景下引入ZooKeeper,在阿里巴巴的生产实践中,应用对ZooKeeper申 请使用的时候要进行严格的场景、容量、SLA需求的评估。

系统配置

设置Xms和Xmx一样大,防止JVM重新调整堆空间大小影响性能。

-server -Xms8g -Xmx8g -Xmn4g

设置DirectByteBuffer内存大小。当DirectByteBuffer占用达到这个值,就会触发Full GC。

-XX:MaxDirectMemorySize=15g

如果不太关心RocketMQ的启动时间,可以设置pre-touch,这样在JVM启动的时候就会分配完整的页空间。

-XX:+AlwaysPreTouch

禁用偏向锁可能减少JVM的停顿,在并发小的时候使用偏向锁有利于提升JVM效率,在高并发场合禁用掉。

-XX:-UseBiasedLocking

推荐使用JDK1.8的G1垃圾回收器。

RocketMQ集群

Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master 发送心跳。Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向 Master、Slave发送心跳。

单Master模式

这种方式风险较大,一旦Broker重启或者宕机时,会导致整个服务不可用。

多Master模式

一个集群无Slave,全是Master,例如2个Master或者3个Master,单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性 会受到影响。

多Master多Slave模式(异步)

每个Master配置一个Slave,有多对Master-Slave,HA采用异步复制方式,主备有短暂消息延迟 (毫秒级),即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,但是会丢失少量消息。

多Master多Slave模式(异步)

每个Master配置一个Slave,有多对Master-Slave,HA采用同步双写方式,即只有主备都写成功, 才向应用返回成功,消息无延迟,服务可用性与数据可用 性都非常高;但是性能比异步复制模式略低(大约低10%左右),发送单个消息的RT会略高,且目前版 本在主节点宕机后,备机不能自动切换为主机。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值