1、MQ简介和部署

⼀、MQ简介

MQ:MessageQueue,消息队列。是在互联⽹中使⽤⾮常⼴泛的⼀系列服务中间件。 这个词可以分两个部分来看,⼀是Message:消息。消息是在不同进程之间传递的数据。这些进程可以部署在同⼀台机器上,也可以分布在
不同机器上。⼆是Queue:队列。队列原意是指⼀种具有FIFO(先进先出)特性的数据结构,是⽤来缓存数据的。对
于消息中间件产品来说,能不能保证FIFO特性,尚值得考量。但是,所有消息队列都是需要具备存储消息,让消息
排队的能⼒。
⼴义上来说,只要能够实现消息跨进程传输以及队列数据缓存,就可以称之为消息队列。例如我们常⽤的QQ、
微信、阿⾥旺旺等就都具备了这样的功能。只不过他们对接的使⽤对象是⼈,⽽我们这⾥讨论的MQ产品需要对接
的使⽤对象是应⽤程序。
MQ的作⽤主要有以下三个⽅⾯:
异步
例⼦:快递员发快递,直接到客户家效率会很低。引⼊菜⻦驿站后,快递员只需要把快递放到菜⻦驿站,就可
以继续发其他快递去了。客户再按⾃⼰的时间安排去菜⻦驿站取快递。
作⽤:异步能提⾼系统的响应速度、吞吐量。
在这里插入图片描述
解耦
例⼦:《Thinking in JAVA》很经典,但是都是英⽂,我们看不懂,所以需要编辑社,将⽂章翻译成其他语
⾔,这样就可以完成英语与其他语⾔的交流。
作⽤:
1、服务之间进⾏解耦,才可以减少服务之间的影响。提⾼系统整体的稳定性以及可扩展性。
2、另外,解耦后可以实现数据分发。⽣产者发送⼀个消息后,可以由⼀个或者多个消费者进⾏消费,并且消
费者的增加或者减少对⽣产者没有影响。
在这里插入图片描述
削峰
例⼦:⻓江每年都会涨⽔,但是下游出⽔⼝的速度是基本稳定的,所以会涨⽔。引⼊三峡⼤坝后,可以把⽔储
存起来,下游慢慢排⽔。
作⽤:以稳定的系统资源应对突发的流量冲击。
在这里插入图片描述

⼆、RocketMQ产品特点

1、RocketMQ介绍

RocketMQ是阿⾥巴巴开源的⼀个消息中间件,在阿⾥内部历经了双⼗⼀等很多⾼并发场景的考验,能够处理亿
万级别的消息。2016年开源后捐赠给Apache,现在是Apache的⼀个顶级项⽬。
早期阿⾥使⽤ActiveMQ,但是,当消息开始逐渐增多后,ActiveMQ的IO性能很快达到了瓶颈。于是,阿⾥开始
关注Kafka。但是Kafka是针对⽇志收集场景设计的,他的⾼级功能并不是很贴合阿⾥的业务场景。尤其当他的
Topic过多时,由于Partition⽂件也会过多,这就会加⼤⽂件索引的耗时,会严重影响IO性能。于是阿⾥才决定⾃
研中间件,最早叫做MetaQ,后来改名成为RocketMQ。最早他所希望解决的最⼤问题就是多Topic下的IO性能压
⼒。但是产品在阿⾥内部的不断改进,RocketMQ开始体现出⼀些不⼀样的优势。

2、RocketMQ特点

当今互联⽹MQ产品众多,其中,影响⼒和使⽤范围最⼤的当数Apache Kafka、RabbitMQ、Apache RocketMQ
以及Apache Plusar。这⼏⼤产品虽然都是典型的MQ产品,但是由于设计和实现上的⼀些差异,造成他们适合于不
同的细分场景。
在这里插入图片描述
其中RocketMQ,孵化⾃阿⾥巴巴。历经阿⾥多年双⼗⼀的严格考验,RocketMQ可以说是从全世界最严苛的⾼
并发场景中摸爬滚打出来的过硬产品,也是少数⼏个在⾦融场景⽐较适⽤的MQ产品。从横向对⽐来看,
RocketMQ与Kafka和RabbitMQ相⽐。RocketMQ的消息吞吐量虽然和Kafka相⽐还是稍有差距,但是却⽐
RabbitMQ⾼很多。在阿⾥内部,RocketMQ集群每天处理的请求数超过5万亿次,⽀持的核⼼应⽤超过3000个。
⽽RocketMQ最⼤的优势就是他天⽣就为⾦融互联⽹⽽⽣。他的消息可靠性相⽐Kafka也有了很⼤的提升,⽽消息
吞吐量相⽐RabbitMQ也有很⼤的提升。另外,RocketMQ的⾼级功能也越来越全⾯,⼴播消费、延迟队列、死信
队列等等⾼级功能⼀应俱全,甚⾄某些业务功能⽐如事务消息,已经呈现出领先潮流的趋势。

三、RocketMQ快速实战

1、快速搭建RocketMQ服务

RocketMQ的官⽹地址: http://rocketmq.apache.org 。在下载⻚⾯可以获取RocketMQ的源码包以及运⾏包。
下载⻚⾯地址:https://rocketmq.apache.org/download
在这里插入图片描述
当前最新的版本是5.x,这是⼀个着眼于云原⽣的新版本,给 RocketMQ 带来了⾮常多很亮眼的新特性。但是⽬
前来看,企业中⽤得还⽐较少。因此,我们这⾥采⽤的还是更为稳定的4.9.5版本。

注:在2020年下半年,RocketMQ新推出了5.0的⼤版本,这对于RocketMQ来说,是⼀个⾥程碑式的⼤版
本。在这个⼤版本中,RocketMQ对整体功能做了⼀次⼤的升级。增加了很多⾮常有⽤的新特性,也对已有 功能重新做了升级。
⽐如在具体功能⽅⾯,在4.x版本中,对于定时消息,只能设定⼏个固定的延迟级别,⽽5.0版本中,已经
可以指定具体的发送时间了。在客户端语⾔⽅⾯,4.x版本,RocketMQ原⽣只⽀持基于Netty框架的Java客户
端。⽽在5.0版本中,增加了对Grpc协议的⽀持,这基本上就解除了对客户端语⾔的限制。在服务端架构⽅
⾯,4.x版本只⽀持固定⻆⾊的普通集群和可以动态切换⻆⾊的Dledger集群,⽽在5.0版本中,增加了 Dledger
Controller混合集群模式,即可以混合使⽤Dledger的集群机制以及 Broker 本地的⽂件管理机制。
但是功能强⼤,同时也意味着问题会很多。所以⽬前来看,企业中直接⽤新版本的还⽐较少。⼩部分使⽤ 新版本的企业,也⼤都是使⽤内部的改造优化版本。

运⾏只需要下载Binary运⾏版本就可以了。 当然,源码包也建议下载下来,后续会进⾏解读。运⾏包下载下来
后,就可以直接解压,上传到服务器上。我们这⾥会上传到/app/rocketmq⽬录。解压后⼏个重要的⽬录如下:
在这里插入图片描述
接下来,RocketMQ建议的运⾏环境需要⾄少12G的内存,这是⽣产环境⽐较理想的资源配置。但是,现在阶
段,如果你的服务器没有这么⼤的内存空间,那么就需要做⼀下调整。进⼊bin⽬录,对其中的runserver.sh和
runbroker.sh两个脚本进⾏⼀下修改,Windows是对应的cmd文件。
使⽤vi runserver.sh指令,编辑这个脚本,找到下⾯的⼀⾏配置,调整Java进程的内存⼤⼩。

JAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx512m -Xmn256m -XX:MetaspaceSize=128m -
XX:MaxMetaspaceSize=320m"

接下来,同样调整runbroker.sh中的内存⼤⼩,⽣产环境不建议调整。
调整完成后,就可以启动RocketMQ服务了。 RocketMQ服务基于Java开发,所以需要提前安装JDK。JDK建议采
⽤1.8版本即可。windows系统需要配置ROCKETMQ_HOME,对象是MQ文件的根路径
RocketMQ的后端服务分为nameserver和broker两个服务

第⼀步:启动nameserver服务。
cd /app/rocketmq/rocketmq-all-4.9.5-bin-release nohup bin/mqnamesrv &

指令执⾏后,会⽣成⼀个nohup.out的⽇志⽂件。在这个⽇志⽂件⾥如果看到下⾯这⼀条关键⽇志,就表示
nameserver服务启动成功了。如果遇到java版本的问题,自行百度,是配载文件默认需要java9的问题,改下配置文件的if判断或者改9为8

Java HotSpot(TM) 64-Bit Server VM warning: Using the DefNew young collector
with the CMS
collector is deprecated and will likely be removed in a future release
Java HotSpot(TM) 64-Bit Server VM warning: UseCMSCompactAtFullCollection is
deprecated and
will likely be removed in a future release.
The Name Server boot success. serializeType=JSON

接下来,可以通过jsp指令进⾏验证。使⽤jps指令后,可以看到有⼀个NamesrvStartup的进程运⾏,也表示
nameserver服务启动完成。

Windows系统的先启动mqnamesrv.cmd: start mqnamesrv.cmd
然后启动mqbroker : start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true

第⼆步:启动broker服务。

启动broker服务之前,要做⼀个⼩⼩的配置。进⼊RocketMQ安装⽬录下的conf⽬录,修改broker.conf⽂件,
在⽂件最后⾯加⼊⼀个配置:

autoCreateTopicEnable=true

这个选项是为了便于进⾏后续实验。他的作⽤是允许 broker 端⾃动创建新的 Topic。
另外,如果你的服务器配置了多张⽹卡,⽐如阿⾥云,腾讯云这样的云服务器,他们通常有内⽹⽹卡和外⽹
⽹卡两张⽹卡,那么需要增加配置brokerIP1属性,指向服务器的外⽹IP 地址,这样才能确保从其他服务器
上访问到RocketMQ 服务。
然后也可以⽤之前的⽅式启动broker服务。启动broker服务的指令是mqbroker
启动完成后,同样检查nohup.out⽇志⽂件,有如下⼀条关键⽇志,就表示broker服务启动正常了。

注:1、在实际服务部署时,通常会将RocketMQ的部署地址添加到环境变量当中。例如使⽤vi
~/.bash_profile指令,添加以下内容。 export
ROCKETMQ_HOME=/app/rocketmq/rocketmq-all-4.9.5-binreleasePATH= R O C K E T M Q H O M E / b i n : ROCKETMQ_HOME/bin: ROCKETMQHOME/bin:PATHexport
PATH 这样就不必每次进⼊RocketMQ的安装⽬录了。直接可以使⽤mqnamesrv 和mqbroker指令。
2、停⽌RocketMQ服务可以通过mqshutdown指令进⾏ mqshutdown namesrv # 关闭nameserver服务
mqshutdown broker # 关闭broker服务

同样使⽤jps指令可以检查服务的启动状态。使⽤jsp指令后,可以看到⼀个名为BrokerStartup的进程,则表示
broker服务启动完成

2、快速实现消息收发

RocketMQ后端服务启动完成后,就可以启动客户端的消息⽣产者和消息消费者进⾏消息转发了。接下来,我们
会先通过RocketMQ提供的命令⾏⼯具快速体验⼀下RocketMQ消息收发的功能。然后,再动⼿搭建⼀个Maven项
⽬,在项⽬中使⽤RocketMQ进⾏消息收发。

1、命令⾏快速实现消息收发

第⼀步:需要配置⼀个环境变量NAMESRV_ADDR,只想我们之前启动的nameserver服务。
通过vi ~/.bash_profile添加以下配置。然后使⽤source ~/.bash_profile让配置⽣效。

export NAMESRV_ADDR='localhost:9876'

第⼆步:通过指令启动RocketMQ的消息⽣产者发送消息。

tools.sh org.apache.rocketmq.example.quickstart.Producer

这个指令会默认往RocketMQ中发送1000条消息。在命令⾏窗⼝可以看到发送消息的⽇志:

SendResult [sendStatus=SEND_OK, msgId=C0A8E88007AC3764951D891CE9A003E7,
offsetMsgId=C0A8E88000002A9F00000000000317BF, messageQueue=MessageQueue
[topic=TopicTest, brokerName=worker1, queueId=1], queueOffset=249]
14:59:33.418 [NettyClientSelector_1] INFO RocketmqRemoting - closeChannel:
close the connection to remote address[127.0.0.1:9876] result: true
14:59:33.423 [NettyClientSelector_1] INFO RocketmqRemoting - closeChannel:
close the connection to remote address[192.168.232.128:10911] result: true

这部分⽇志中,并没有打印出发送了什么消息。上⾯SendResult开头部分是消息发送到Broker后的结果。最后
两⾏⽇志表示消息⽣产者发完消息后,服务正常关闭了。
第三步:可以启动消息消费者接收之前发送的消息

tools.sh org.apache.rocketmq.example.quickstart.Consumer

消费者启动完成后,可以看到消费到的消息

ConsumeMessageThread_19 Receive New Messages: [MessageExt
[brokerName=worker1, queueId=2, storeSize=203, queueOffset=53, sysFlag=0,
bornTimestamp=1606460371999, bornHost=/192.168.232.128:43436,
storeTimestamp=1606460372000, storeHost=/192.168.232.128:10911,
msgId=C0A8E88000002A9F000000000000A7AE, commitLogOffset=42926,
bodyCRC=1968636794, reconsumeTimes=0, preparedTransactionOffset=0,
toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0,
MAX_OFFSET=250, CONSUME_START_TIME=1606460450150,
UNIQ_KEY=C0A8E88007AC3764951D891CE41F00D4, CLUSTER=DefaultCluster,
WAIT=true, TAGS=TagA}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107,
101, 116, 77, 81, 32, 50, 49, 50], transactionId='null'}]]

每⼀条这样的⽇志信息就表示消费者接收到了⼀条消息。
这个Consumer消费者的指令并不会主动结束,他会继续挂起,等待消费新的消息。我们可以使⽤CTRL+C停⽌
该进程

注:在RocketMQ提供的这个简单示例中并没有打印出传递的消息内容,⽽是打印出了消息相关的很多重要 的属性。 其中有⼏个⽐较重要的属性:
brokerId,brokerName,queueId,msgId,topic,cluster。这些属性的作⽤会在后
续⼀起分享,这⾥你不妨先找⼀下这些属性是什么,消费者与⽣产者之间有什么样的对应关系。

2、搭建Maven客户端项⽬

之前的步骤实际上是在服务器上快速验证RocketMQ的服务状态,接下来我们动⼿搭建⼀个RocketMQ的客户端
应⽤,在实际应⽤中集成使⽤RocketMQ。
第⼀步:创建⼀个标准的maven项⽬,在pom.xml中引⼊以下核⼼依赖

<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.9.5</version>
</dependency>

第⼆步:就可以直接创建⼀个简单的消息⽣产者

public class Producer {
public static void main(String[] args) throws MQClientException,
InterruptedException {
//初始化⼀个消息⽣产者
DefaultMQProducer producer = new
DefaultMQProducer("please_rename_unique_group_name");
// 指定nameserver地址
producer.setNamesrvAddr("192.168.232.128:9876");
// 启动消息⽣产者服务
producer.start();
for (int i = 0; i < 2; i++) {
try {
// 创建消息。消息由Topic,Tag和body三个属性组成,其中Body就是消息内容
Message msg = new Message("TopicTest","TagA",("Hello RocketMQ "
+i).getBytes(RemotingHelper.DEFAULT_CHARSET));
//发送消息,获取发送结果
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
 } catch (Exception e) {
e.printStackTrace();
Thread.sleep(1000);
 }
 }
//消息发送完后,停⽌消息⽣产者服务。
producer.shutdown();
 }
}

运⾏其中的main⽅法,就会往RocketMQ中发送两条消息。在这个实现过程中,需要注意⼀下的是对于⽣产者,
需要指定对应的nameserver服务的地址,这个地址需要指向你⾃⼰的服务器。
第三步:创建⼀个消息消费者接收RocketMQ中的消息。

public class Consumer {
public static void main(String[] args) throws InterruptedException,
MQClientException {
//构建⼀个消息消费者
DefaultMQPushConsumer consumer = new
DefaultMQPushConsumer("please_rename_unique_group_name_4");
//指定nameserver地址
consumer.setNamesrvAddr("192.168.232.128:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
// 订阅⼀个感兴趣的话题,这个话题需要与消息的topic⼀致
consumer.subscribe("TopicTest", "*");
// 注册⼀个消息回调函数,消费到消息后就会触发回调。
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt>
msgs,ConsumeConcurrentlyContext context) {
 msgs.forEach(messageExt -> {
try {
System.out.println("收到消息:"+new String(messageExt.getBody(),
RemotingHelper.DEFAULT_CHARSET));
 } catch (UnsupportedEncodingException e) {}
 });
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
 }
 });
//启动消费者服务
consumer.start();
System.out.print("Consumer Started");
 }
}

运⾏其中的main⽅法后,就可以启动⼀个RocketMQ消费者,接收之前发到RocketMQ上的消息,并将消息内容
打印出来。在这个实现过程中,需要重点关注的有两点。⼀是对于消费者,同样需要指定nameserver的地址。⼆
是消费者需要在RocketMQ中订阅具体的Topic,只有发送到这个Topic上的消息才会被这个消费者接收到
这样,通过⼏个简单的步骤,我们就完成了RocketMQ的应⽤集成。从这个过程中可以看到,RocketMQ的使⽤
是⽐较简单的。但是这并不意味着这⼏个简单的步骤就⾜够搭建⼀个⽣产级别的RocketMQ服务。接下来,我们会
⼀步步把我们这个简单的RocketMQ服务往⼀个⽣产级别的服务集群推进。

3、搭建RocketMQ可视化管理服务

在之前的简单实验中,RocketMQ都是以后台服务的⽅式在运⾏,我们并不很清楚RocketMQ是如何运⾏的。
RocketMQ的社区就提供了⼀个图形化的管理控制台Dashboard,可以⽤可视化的⽅式直接观测并管理RocketMQ
的运⾏过程。
Dashboard服务并不在RocketMQ的运⾏包中,需要到RocketMQ的官⽹下载⻚⾯单独下载。
在这里插入图片描述
这⾥只提供了源码,并没有提供直接运⾏的jar包。将源码下载下来后,需要解压并进⼊对应的⽬录,使⽤
maven进⾏编译。(需要提前安装maven客户端)

mvn clean package -Dmaven.test.skip=true

编译完成后,在源码的target⽬录下会⽣成可运⾏的jar包rocketmq-dashboard-1.0.1-SNAPSHOT.jar。接下来
可以将这个jar包上传到服务器上。我们上传到/app/rocketmq/rocketmq-dashboard⽬录下
接下来我们需要在jar包所在的⽬录下创建⼀个application.yml配置⽂件,在配置⽂件中做如下配置:

rocketmq:
 config:
 namesrvAddrs:
 - 192.168.232.128:9876
``主要是要指定nameserver的地址。
注:关于这个配置⽂件中更多的配置选项,可以参考⼀下dashboard源码当中的application.yml配置⽂件。
接下来就可以通过java指令执⾏这个jar包,启动管理控制台服务。
java -jar rocketmq-dashboard-1.0.1-SNAPSHOT.jar
应⽤启动完成后,会在服务器上搭建起⼀个web服务,我们就可以通过访问http://192.168.232.128:8080查看到
管理⻚⾯。`
## 4、升级分布式集群
之前我们⽤⼀台Linux服务器,快速搭建起了⼀整套RocketMQ的服务。但是很明显,这样搭建的服务是⽆法放
到⽣产环境上去⽤的。⼀旦nameserver服务或者broker服务出现了问题,整个RocketMQ就⽆法正常⼯作。⽽且
更严重的是,如果服务器出现了问题,⽐如磁盘坏了,那么存储在磁盘上的数据就会丢失。这时RocketMQ暂存到
磁盘上的消息也会跟着丢失,这个问题就⾮常严重了。因此,我们需要搭建⼀个分布式的RocketMQ服务集群,来
防⽌单点故障问题。
RocketMQ的分布式集群基于主从架构搭建。在多个服务器组成的集群中,指定⼀部分节点作为Master节点,负
责响应客户端的请求。指令另⼀部分节点作为Slave节点,负责备份Master节点上的数据,这样,当Master节点出
现故障时,在Slave节点上可以保留有数据备份,⾄少保证数据不会丢失。
整个集群⽅案如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/5fcf49391cb547ecaef96b4194159de5.png)
接下来我们准备三台相同的Linux服务器,搭建⼀下RocketMQ的分布式集群。为了更清晰的描述这三台服务器
上的操作,我们给每个服务器指定⼀个机器名。

```java
cat /etc/hosts
192.168.232.128 worker1
192.168.232.129 worker2
192.168.232.130 worker3

为了便于观察,我们这次搭建⼀个2主2从的RocketMQ集群,并将主节点和节点都分别部署在不同的服务器上。
预备的集群规划情况如下
在这里插入图片描述
第⼀步:部署nameServer服务。
nameServer服务不需要做特别的配置,按照之前的步骤,在三台服务器上都分别部署nameServer服务即可。
第⼆步:对Broker服务进⾏集群配置。
这⾥需要修改RocketMQ的配置⽂件,对broker服务做⼀些集群相关的参数部署。这些配置⽂件并不需要我们⼿
动进⾏创建,在RocketMQ运⾏包的conf⽬录下,提供了多种集群的部署配置⽂件模板。
2m-noslave: 2主⽆从的集群参考配置。这种集群存在单点故障。
2m-2s-async和2m-2s-sync: 2主2从的集群参考配置。其中async和sync表示主节点与从节点之间是同步同步
还是异步同步。关于这两个概念,会在后续章节详细介绍
dledger: 具备主从切换功能的⾼可⽤集群。集群中的节点会基于Raft协议随机选举出⼀个Leader,其作⽤类
似于Master节点。其他的节点都是follower,其作⽤类似于Slave节点。
我们这次采⽤2m-2s-async的⽅式搭建集群,需要在worker2和worker3上修改这个⽂件夹下的配置⽂件。
1> 配置第⼀组broker-a服务
在worker2机器上配置broker-a的MASTER服务,需要修改conf/2m-2s-async/broker-a.properties。示例配置
如下:

#所属集群名字,名字⼀样的节点就在同⼀个集群内
brokerClusterName=rocketmq-cluster
#broker名字,名字⼀样的节点就是⼀组主从节点。
brokerName=broker-a
#brokerid,0就表示是Master>0的都是表示 Slave
brokerId=0
#nameServer地址,分号分割
namesrvAddr=worker1:9876;worker2:9876;worker3:9876
#是否允许 Broker ⾃动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
deleteWhen=04
fileReservedTime=120
#存储路径
storePathRootDir=/app/rocketmq/store
storePathCommitLog=/app/rocketmq/store/commitlog
storePathConsumeQueue=/app/rocketmq/store/consumequeue
storePathIndex=/app/rocketmq/store/index
storeCheckpoint=/app/rocketmq/store/checkpoint
abortFile=/app/rocketmq/store/abort
#Broker 的⻆⾊
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
#Broker 对外服务的监听端⼝
listenPort=10911

这⾥对⼏个需要重点关注的属性,做下简单介绍:
brokerClusterName: 集群名。RocketMQ会将同⼀个局域⽹下所有brokerClusterName相同的服务⾃动组成
⼀个集群,这个集群可以作为⼀个整体对外提供服务
brokerName: Broker服务名。同⼀个RocketMQ集群当中,brokerName相同的多个服务会有⼀套相同的数
据副本。同⼀个RocketMQ集群中,是可以将消息分散存储到多个不同的brokerName服务上的。
brokerId: RocketMQ中对每个服务的唯⼀标识。RocketMQ对brokerId定义了⼀套简单的规则,master节点
需要固定配置为0,负责响应客户端的请求。slave节点配置成其他任意数字,负责备份master上的消息。
brokerRole: 服务的⻆⾊。这个属性有三个可选项:ASYNC_MASTER,SYNC_MASTER和SLAVE。其中,
ASYNC_MASTER和SYNC_MASTER表示当前节点是master节点,⽬前暂时不⽤关⼼他们的区别。SLAVE则表
示从节点。
namesrvAddr: nameserver服务的地址。nameserver服务默认占⽤9876端⼝。多个nameserver地址⽤;隔
开。
接下来在worekr3上配置broker-a的SLAVE服务。需要修改conf/2m-2s-async/broker-a-s.properties。示例配置
如下

#所属集群名字,名字⼀样的节点就在同⼀个集群内
brokerClusterName=rocketmq-cluster
#broker名字,名字⼀样的节点就是⼀组主从节点。
brokerName=broker-a
#brokerid,0就表示是Master>0的都是表示 Slave
brokerId=1
#nameServer地址,分号分割
namesrvAddr=worker1:9876;worker2:9876;worker3:9876
#是否允许 Broker ⾃动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
deleteWhen=04
fileReservedTime=120
#存储路径
storePathRootDir=/app/rocketmq/storeSlave
storePathCommitLog=/app/rocketmq/storeSlave/commitlog
storePathConsumeQueue=/app/rocketmq/storeSlave/consumequeue
storePathIndex=/app/rocketmq/storeSlave/index
storeCheckpoint=/app/rocketmq/storeSlave/checkpoint
abortFile=/app/rocketmq/storeSlave/abort
#Broker 的⻆⾊
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
#Broker 对外服务的监听端⼝
listenPort=11011

其中关键是brokerClusterName和brokerName两个参数需要与worker2上对应的broker-a.properties配置匹
配。brokerId配置0以为的数字。然后brokerRole配置为SLAVE。
这样,第⼀组broker服务就配置好了。
2> 配置第⼆组borker-b服务
与第⼀组broker-a服务的配置⽅式类似,在worker3上配置broker-b的MASTER服务。需要修改conf/2m-2sasync/broker-b.properties⽂件,配置示例如下:

#所属集群名字,名字⼀样的节点就在同⼀个集群内
brokerClusterName=rocketmq-cluster
#broker名字,名字⼀样的节点就是⼀组主从节点。
brokerName=broker-b
#brokerid,0就表示是Master>0的都是表示 Slave
brokerId=0
#nameServer地址,分号分割
namesrvAddr=worker1:9876;worker2:9876;worker3:9876
#是否允许 Broker ⾃动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
deleteWhen=04
fileReservedTime=120
#存储路径
storePathRootDir=/app/rocketmq/store
storePathCommitLog=/app/rocketmq/store/commitlog
storePathConsumeQueue=/app/rocketmq/store/consumequeue
storePathIndex=/app/rocketmq/store/index
storeCheckpoint=/app/rocketmq/store/checkpoint
abortFile=/app/rocketmq/store/abort
#Broker 的⻆⾊
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
#Broker 对外服务的监听端⼝
listenPort=10911

在worker2上配置broker-b的SLAVE服务。需要修改conf/2m-2s-async/broker-b-s.properties⽂件,配置示例
如下:

#所属集群名字,名字⼀样的节点就在同⼀个集群内
brokerClusterName=rocketmq-cluster
#broker名字,名字⼀样的节点就是⼀组主从节点。
brokerName=broker-b
#brokerid,0就表示是Master>0的都是表示 Slave
brokerId=1
#nameServer地址,分号分割
namesrvAddr=worker1:9876;worker2:9876;worker3:9876
#是否允许 Broker ⾃动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
deleteWhen=04
fileReservedTime=120
#存储路径
storePathRootDir=/app/rocketmq/storeSlave
storePathCommitLog=/app/rocketmq/storeSlave/commitlog
storePathConsumeQueue=/app/rocketmq/storeSlave/consumequeue
storePathIndex=/app/rocketmq/storeSlave/index
storeCheckpoint=/app/rocketmq/storeSlave/checkpoint
abortFile=/app/rocketmq/storeSlave/abort
#Broker 的⻆⾊
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
#Broker 对外服务的监听端⼝
listenPort=11011

这样就完成了2主2从集群的配置。配置过程汇总有⼏个需要注意的配置项:
store开头的⼀系列配置:表示RocketMQ的存盘⽂件地址。在同⼀个机器上需要部署多个Broker服务时,不
同服务的存储⽬录不能相同。
listenPort:表示Broker对外提供服务的端⼝。这个端⼝默认是10911。在同⼀个机器上部署多个Broker服务
时,不同服务占⽤的端⼝也不能相同。
如果你使⽤的是多⽹卡的服务器,⽐如阿⾥云上的云服务器,那么就需要在配置⽂件中增加配置⼀个
brokerIP1属性,指向所在机器的外⽹⽹卡地址。
第三步:启动Broker服务
集群配置完成后,需要启动Broker服务。与之前启动broker服务稍有不同,启动时需要增加-c参数,指向我们修
改的配置⽂件。
在worker2上启动broker-a的master服务和broker-b的slave服务:

cd /app/rocketmq/rocketmq-all-4.9.5-bin-release
nohup bin/mqbroker -c ../conf/2m-2s-async/broker-a.properties &
nohup bin/mqbroker -c ../conf/2m-2s-async/broker-b-s.properties &

在worker3上启动broker-b的master服务和broker-a的slave服务:

cd /app/rocketmq/rocketmq-all-4.9.5-bin-release
nohup bin/mqbroker -c ../conf/2m-2s-async/broker-b.properties &
nohup bin/mqbroker -c ../conf/2m-2s-async/broker-a-s.properties &

第四步:检查集群服务状态
对于服务的启动状态,我们依然可以⽤之前介绍的jps指令以及nohup.out⽇志⽂件进⾏跟踪。不过,在
RocketMQ的bin⽬录下,也提供了mqadmin指令,可以通过命令⾏的⽅式管理RocketMQ集群。
例如下⾯的指令可以查看集群broker集群状态。通过这个指令可以及时了解集群的运⾏状态。

[oper@worker1 bin]$ cd /app/rocketmq/rocketmq-all-4.9.5-bin-release/bin
[oper@worker1 bin]$ mqadmin clusterList
RocketMQLog:WARN No appenders could be found for logger
(io.netty.util.internal.InternalThreadLocalMap).
RocketMQLog:WARN Please initialize the logger system properly.
#Cluster Name #Broker Name #BID #Addr #Version 
 #InTPS(LOAD) #OutTPS(LOAD) #PCWait(ms) #Hour #SPACE
rocketmq-cluster broker-a 0 192.168.232.129:10911 V4_9_1 
0.00(0,0ms) 0.00(0,0ms) 0 3425.28 0.3594
rocketmq-cluster broker-a 1 192.168.232.130:11011 V4_9_1 
0.00(0,0ms) 0.00(0,0ms) 0 3425.28 0.3607
rocketmq-cluster broker-b 0 192.168.232.130:10911 V4_9_1 
0.00(0,0ms) 0.00(0,0ms) 0 3425.27 0.3607
rocketmq-cluster broker-b 1 192.168.232.129:11011 V4_9_1 
0.00(0,0ms) 0.00(0,0ms) 0 3425.27 0.3594

mqadmin指令还提供了⾮常丰富的管理功能。你可以尝试直接使⽤mqadmin指令,就会列出mqadmin⽀持的
所有管理指令。如果对某⼀个指令不会使⽤,还可以使⽤mqadmin help 指令查看帮助。
另外,之前搭建的dashboard也是集群服务状态的很好的⼯具。只需要在之前搭建Dashboard时创建的配置⽂件
中增加指定nameserver地址即可。

rocketmq:
 config:
 namesrvAddrs:
 - worker1:9876
 - worker2:9876
 - worker3:9876

启动完成后,在集群菜单⻚就可以看到集群的运⾏情况
在这里插入图片描述

5、升级⾼可⽤集群

主从架构的RocketMQ集群,由于给每个broker服务配置了⼀个或多个slave备份服务,可以保证当broker服务
出现问题时,broker上的消息不会丢失。但是,这种主从架构的集群却也有⼀个不⾜的地⽅,那就是不具备服务⾼
可⽤。
这⾥所说的服务⾼可⽤,并不是并不是指整个RocketMQ集群就不能对外提供服务了,⽽是指集群中的消息就不
完整了。实际上,当RocketMQ集群中的broker宕机后,整个集群会⾃动进⾏broker状态感知。后续客户端的各种
请求,依然可以转发到其他正常的broker上。只不过,原本保存在当前broker上的消息,就⽆法正常读取了,需要
等到当前broker服务重启后,才能重新被消息消费者读取。
当⼀个broker上的服务宕机后,我们可以从对应的slave服务上找到broker上所有的消息。但是很可惜,主从架
构中各个服务的⻆⾊都是固定了的,slave服务虽然拥有全部的数据,但是它没办法升级成为master服务去响应客
户端的请求,依然只是傻傻等待master服务重启后,继续做它的数据备份⼯作。
这时,我们⾃然就希望这个slave服务可以升级成为master服务,继续响应客户端的各种请求,这样整个集群的
消息服务就不会有任何中断。⽽RocketMQ提供的Dledger集群,就是具备⻆⾊⾃动转换功能的⾼可⽤集群。
整个集群结构如下图所示:
在这里插入图片描述
在Dledger集群中,就不再单独指定各个broker的服务,⽽是由这些broker服务⾃⾏进⾏选举,产⽣⼀个Leader
⻆⾊的服务,响应客户端的各种请求。⽽其他的broker服务,就作为Follower⻆⾊,负责对Leader上的数据进⾏
备份。当然,Follower所要负责的事情,⽐主从架构中的SLAVE⻆⾊会要复杂⼀点,因为这种节点选举是在后端不
断进⾏的,他们需要随时做好升级成Leader的准备。
Dledger集群的选举是通过Raft协议进⾏的,Raft协议是⼀种多数同意机制。也就是每次选举需要有集群中超过
半数的节点确认,才能形成整个集群的共同决定。同时,这也意味着在Dledger集群中,只要有超过半数的节点能
够正常⼯作,那么整个集群就能正常⼯作。因此,在部署Dledger集群时,通常都是部署奇数台服务,这样可以让
集群的容错性达到最⼤。
接下来,我们就⽤之前准备的3台服务器,搭建⼀个3个节点的Dledger集群。在这个集群中,只需要有2台
Broker服务正常运⾏,这个集群就能正常⼯作。
第⼀步:部署nameserver
这⼀步和之前部署主从集群没有区别,不需要做过多的配置,直接在三台服务器上启动nameserver服务即可。
实际上,如果你是从上⼀个主从架构开始搭建起来的话,那么nameserver集群都不需要重新启动,nameserver
会⾃动感知到broker的变化。
第⼆步:对Broker服务进⾏集群配置。
对于Dledger集群的配置,RocketMQ依然贴⼼的给出了完整的示例,不需要强⾏记忆。
在conf/dledger⽬录下,RocketMQ默认给出了三个配置⽂件,这三个配置⽂件可以在单机情况下直接部署成⼀
个具有三个broker服务的Dledger集群,我们只需要按照这个配置进⾏修改即可。
注:在RocketMQ运⾏包的bin/dledger⽬录下,RocketMQ还提供了⼀个fast-try.sh脚本。这个脚本会指定
conf/deldger⽬录下的配置⽂件,直接启动有三个broker服务的Dledger集群。每个集群指定的内存⼤⼩占
⽤1G。
接下来我们可以在三台机器的conf/dledger⽬录下,都创建⼀个broker.conf⽂件,对每个broker服务进⾏配
置。
worker1的broker.conf配置示例

brokerClusterName = RaftCluster
brokerName=RaftNode00
listenPort=30911
namesrvAddr=worker1:9876;worker2:9876;worker3:9876
storePathRootDir=/app/rocketmq/storeDledger/
storePathCommitLog=/app/rocketmq/storeDledger/commitlog
storePathConsumeQueue=/app/rocketmq/storeDledger/consumequeue
storePathIndex=/app/rocketmq/storeDledger/index
storeCheckpoint=/app/rocketmq/storeDledger/checkpoint
abortFile=/app/rocketmq/storeDledger/abort
enableDLegerCommitLog=true
dLegerGroup=RaftNode00
dLegerPeers=n0-worker1:40911;n1-worker2:40911;n2-worker3:40911
## must be unique
dLegerSelfId=n0
sendMessageThreadPoolNums=16

worker2的broker.conf配置示例:
brokerClusterName = RaftCluster

brokerName=RaftNode00
listenPort=30911
namesrvAddr=worker1:9876;worker2:9876;worker3:9876
storePathRootDir=/app/rocketmq/storeDledger/
storePathCommitLog=/app/rocketmq/storeDledger/commitlog
storePathConsumeQueue=/app/rocketmq/storeDledger/consumequeue
storePathIndex=/app/rocketmq/storeDledger/index
storeCheckpoint=/app/rocketmq/storeDledger/checkpoint
abortFile=/app/rocketmq/storeDledger/abort
enableDLegerCommitLog=true
dLegerGroup=RaftNode00
dLegerPeers=n0-worker1:40911;n1-worker2:40911;n2-worker3:40911
## must be unique
dLegerSelfId=n1
sendMessageThreadPoolNums=16 

worker3的broker.conf配置示例:

brokerClusterName = RaftCluster
brokerName=RaftNode00
listenPort=30911
namesrvAddr=worker1:9876;worker2:9876;worker3:9876
storePathRootDir=/app/rocketmq/storeDledger/
storePathCommitLog=/app/rocketmq/storeDledger/commitlog
storePathConsumeQueue=/app/rocketmq/storeDledger/consumequeue
storePathIndex=/app/rocketmq/storeDledger/index
storeCheckpoint=/app/rocketmq/storeDledger/checkpoint
abortFile=/app/rocketmq/storeDledger/abort
enableDLegerCommitLog=true
dLegerGroup=RaftNode00
dLegerPeers=n0-worker1:40911;n1-worker2:40911;n2-worker3:40911
## must be unique
dLegerSelfId=n2
sendMessageThreadPoolNums=16 

这⾥对⼏个需要重点关注的配置项,做下介绍:

enableDLegerCommitLog: 是否启动Dledger。true表示启动 namesrvAddr:
指定nameserver地址 dLedgerGroup: Dledger Raft Group的名字,建议跟brokerName保持⼀致。
dLedgerPeers: Dledger Group内各个服务节点的地址及端⼝信息。同⼀个Group内的各个节点配置必须要保 持⼀致。
dLedgerSelfId: Dledger节点ID,必须属于dLedgerPeers中的⼀个。同⼀个Group内的各个节点必须不能重 复。
sendMessageThreadPoolNums:dLedger内部发送消息的线程数,建议配置成cpu核⼼数。

store开头的⼀系列配置: 这些是配置dLedger集群的消息存盘⽬录。如果你是从主从架构升级成为dLedger
架构,那么这个地址可以指向之前搭建住主从架构的地址。dLedger集群会兼容主从架构集群的消息格式,只
不过主从架构的消息⽆法享受dLedger集群的两阶段同步功能。
第三步:启动broker服务
和启动主从架构的broker服务⼀样,我们只需要在启动broker服务时,指定配置⽂件即可。在三台服务器上分别
执⾏以下指令,启动broker服务。

cd /app/rocketmq/rocketmq-all-4.9.5-bin-release/
nohup bin/mqbroker -c conf/dledger/broker.conf &

第四步:检查集群服务状态
我们可以在Dashboard控制台的集群菜单⻚看到Dledger集群的运⾏状况。在这里插入图片描述
从整个配置过程中可以看到,我们并没有指定每个节点的⻆⾊,⽽Dledger集群就⾃动将192.168.232.129也就
是worker2上的broker服务选举成了master。
接下来如果你有兴趣,可以⾃⼰尝试下停⽌woker2上的broker服务,再重新观察集群的运⾏状况。RocketMQ
会在发现worker2服务宕机后,很快的选举产⽣新的master节点。但具体选举出worker1还是worker3作为
master,则是随机的。
但是,如果你尝试继续停⽌worker1或worker3上的broker服务,那么集群中宕机的broker服务就超过了半数,
也就是两台。这时这个Dledger集群就选举不出master节点,也就⽆法正常⼯作了。
关于Dledger集群的⼀些补充
Dledger集群机制是RocketMQ⾃4.5版本开始⽀持的⼀个重要特性。他其实是由OpenMessage组织带⼊
RocketMQ的⼀个系列框架。他是⼀个为⾼可⽤、⾼性能、⾼可靠的分布式存储系统提供基础⽀持的组件。他做的
事情主要有两个,⼀是在集群中选举产⽣master节点。RocketMQ集群需要⽤这个master节点响应客户端的各种
请求。⼆是在各种复杂的分布式场景下,保证CommitLog⽇志⽂件在集群中的强⼀致性。
以下是ChatGPT对于Dledger的功能描述
RocketMQ是⼀款分布式消息队列系统,主要⽤于处理⼤量数据的实时传输和处理。在RocketMQ中,
DLedger是⼀个为⾼可⽤、⾼性能、⾼可靠的分布式存储系统提供基础⽀持的组件。DLedger集群主要具有
以下功能:

  1. 数据复制:DLedger集群通过raft协议来保证数据的⼀致性。在集群中,每个节点都维护⼀个相同的数 据副本,以确保当某个节点出现故障时,数据不会丢失。
  2. 容错性:DLedger集群具有很⾼的容错性。即使集群中的部分节点发⽣故障,只要集群中有⼤多数节点 (即超过半数)仍在正常⼯作,整个集群将继续提供服务。
  3. ⾼可⽤性:DLedger集群通过负载均衡和热备份等机制,确保在节点故障时能够快速切换到其他正常节 点,提⾼整个系统的可⽤性。
  4. 分布式锁:DLedger集群提供分布式锁功能,可以解决分布式系统中的资源争⽤问题,实现跨节点的资 源同步。
  5. 强⼀致性:DLedger集群通过使⽤Raft⼀致性协议,确保在多个副本节点之间同步数据,保证数据的强 ⼀致性。
  6. ⾼性能:DLedger集群⽀持⽔平扩展,可以通过增加节点来提⾼系统的吞吐量和存储能⼒,以满⾜不断 增⻓的业务需求。
  7. 易于管理:DLedger集群提供了⼀系列管理和监控功能,使运维⼈员可以更⽅便地掌握系统的运⾏状 况,及时发现和解决问题。

总之,RocketMQ的DLedger集群具有⾼可⽤、⾼性能、⾼可靠等特点,为分布式消息队列系统提供了坚实
的基础。
其背后的核⼼就是Raft协议。这是⼀种强⼤的分布式选举算法,其核⼼是只要集群中超过半数的节点作出的共同
决议,就认为是集群最终的共同决议。
Raft协议通过投票机制保持数据⼀致性。详细的细节,我们这⾥不做过多讨论,只是给你介绍⼀下Raft协议⼀个
很强⼤的地⽅,就是他解决了分布式集群中的脑裂问题。
关于脑裂问题,这是在集群选举过程中⼀个出现概率不⾼,但是让很多⼈头疼的问题。在分布式集群内,有可能
会由于⽹络波动或者其他⼀些不稳定因素,造成集群内节点之间短时间通信不畅通。这时就容易在集群内形成多个
包含多个节点的⼩集合。这些集合就会独⽴进⾏选举,各⾃产⽣新的Master节点。当⽹络恢复畅通后,集群中就有
了多个Master节点。当集群中出现多个Master节点后,其他节点就不知道要听从谁的指令了,从⽽造成集群整体
⼯作瘫痪。也就是俗话说的“⼀⼭不容⼆⻁”。脑裂问题在以Zookeeper为代表的早前⼀代分布式⼀致性产品中,是
⼀个⾮常头疼的问题。⽽Raft协议对于脑裂问题,会采⽤随机休眠的机制,彻底解决脑裂问题。RocketMQ是Raft
协议的⼀个重要的成功示例。Kafka也在之后基于Raft协议,⾃⾏实现了Kraft集群机制。
同样,附上ChatGPT对于脑裂问题的介绍,供你参考:

问题:Dledger集群如何防⽌集群脑裂问题?
DLedger集群通过使⽤Raft协议来防⽌集群脑裂(split-brain)问题。脑裂问题是指在分布式系统中,由于⽹
络分区或其他原因导致集群被分割成两个或多个⼦集群,各⾃独⽴运⾏且⽆法感知到其他⼦集群的存在。这
可能导致数据不⼀致和错误决策。Raft协议采⽤了⼀系列措施来避免脑裂问题的发⽣:

  1. 选举机制:Raft协议的基础是选举出⼀个领导者(Leader),其他节点(Follower)都从领导者获取数 据。选举过程要求候选⼈必须获得集群中⼤多数节点的⽀持才能成为领导者。这确保了集群中只能有⼀ 个领导者,从⽽避免了脑裂问题。
  2. 任期(Term):Raft协议为每个选举周期设置了⼀个递增的任期编号。任期编号⽤于标识当前的领导 者,确保旧的领导者不会再次被选为领导者。如果⼀个节点发现⾃⼰的任期⼩于其他节点,那么它会停 ⽌当前的⼯作并更新⾃⼰的任期。
  3. ⼼跳机制:领导者会定期向其他节点发送⼼跳消息,以保持与Follower节点的连接。当⼀个节点⻓时间 未收到领导者的⼼跳时,它会认为当前领导者失效,并启动新⼀轮选举。这确保了当领导者出现故障 时,系统能够快速地选出新的领导者。
  4. ⽇志复制:领导者负责将数据更新(⽇志条⽬)复制到其他节点。Follower节点只有在收到领导者的⽇ 志条⽬并将其写⼊本地⽇志后,才会响应客户端的请求。这确保了在发⽣脑裂情况下,不会出现多个节 点试图同时修改同⼀份数据的情况。

通过以上措施,DLedger集群利⽤Raft协议避免了脑裂问题的发⽣,保证了系统的⾼可⽤性和数据⼀致性。
注:Dledger集群由于会接管RocketMQ原⽣的⽂件写⼊,所以,Dledger集群的⽂件写⼊速度⽐RocketMQ的原
⽣写⼊⽅式是要慢⼀点的。这会对RocketMQ的性能产⽣⼀些影响。所以,当前版本的Dledger集群在企业中⽤得
并不是太多。5.0版本对Dledger集群抽出了⼀种Dledger Controller模式,也就是只⽤Dledger集群的选举功能,
⽽不⽤他的Commit⽂件写⼊功能,这样性能可以得到⼀定的提升。

四、总结RocketMQ的运⾏架构

通过之前的⼀系列实验,相信你对RocketMQ的运⾏机制有了⼀个⼤概的了解。接下来我们结合⼀下之前实验的
过程,来理解⼀下RocketMQ的运⾏架构。
下图是RocketMQ运⾏时的整体架构:在这里插入图片描述
接下来,我们就完整梳理⼀下RocketMQ中各个组件的作⽤:
1、nameServer 命名服务
在我们之前的实验过程中,你会发现,nameServer不依赖于任何其他的服务,⾃⼰独⽴就能启动。并且,不管
是broker还是客户端,都需要明确指定nameServer的服务地址。以⼀台电脑为例,nameServer可以理解为是整
个RocketMQ的CPU,整个RocketMQ集群都要在CPU的协调下才能正常⼯作。
2、broker 核⼼服务
从之前的实验过程中你会发现,broker是RocketMQ中最为娇贵的⼀个组件。RockeMQ提供了各种各样的重要
设计来保护broker的安全。同时broker也是RocketMQ中配置最为繁琐的部分。同样以电脑为例,broker就是整个
RocketMQ中的硬盘、显卡这⼀类的核⼼硬件。RocketMQ最核⼼的消息存储、传递、查询等功能都要由broker提
供。
3、client 客户端
Client包括消息⽣产者和消息消费者。同样以电脑为例,Client可以认为是RocketMQ中的键盘、⿏标、显示器这
类的输⼊输出设备。⿏标、键盘输⼊的数据需要传输到硬盘、显卡等硬件才能进⾏处理。但是键盘、⿏标是不能直
接将数据输⼊到硬盘、显卡的,这就需要CPU进⾏协调。通过CPU,⿏标、键盘就可以将输⼊的数据最终传输到核
⼼的硬件设备中。经过硬件设备处理完成后,再通过CPU协调,显示器这样的输出设备就能最终从核⼼硬件设备中
获取到输出的数据。

五、理解RocketMQ的消息模型

⾸先:我们先来尝试往RocketMQ中发送⼀批消息。
在上⼀章节提到,RocketMQ提供了⼀个测试脚本tools.sh,⽤于快速测试RocketMQ的客户端。
在服务器上配置了⼀个NAMESRV_ADDR环境变量后,就可以直接使⽤RocketMQ提供的tools.sh脚本,调⽤
RocketMQ提供的Producer示例。
这⾥调⽤的Producer示例实际上是在RocketMQ安装⽬录下的lib/rocketmq-example-4.9.5.jar中包含的⼀个测
试类。tools.sh脚本则是提供Producer类的运⾏环境。

tools.sh org.apache.rocketmq.example.quickstart.Producer

Producer这个测试类,会往RocketMQ中发送⼀千条测试消息。发送消息后,我们可以在控制台看到很多如下的
⽇志信息。

SendResult [sendStatus=SEND_OK, msgId=7F000001426E28A418FC6545DFD803E7,
offsetMsgId=C0A8E88100002A9F0000000000B4F6E5, messageQueue=MessageQueue
[topic=TopicTest, brokerName=broker-a, queueId=2], queueOffset=124]

这是RocketMQ的Broker服务端给消息⽣产者的响应。这个响应信息代表的是Broker服务端已经正常接收并保存
了消息⽣产者发送的消息。这⾥⾯提到了很多topic、messageQueue等概念,这些是什么意思呢?我们不妨先去
RocketMQ的DashBoard控制台看⼀下RocketMQ的Broker是如何保存这些消息的。
访问DashBoard上的“主题”菜单,可以看到多了⼀个名为TopicTest的主题。在这里插入图片描述
这个TopicTest就是我们之前运⾏的Producer创建的主题。点击“状态”按钮,可以看到TopicTest上的消息分布。
在这里插入图片描述
从这⾥可以看到,TopicTest这个话题下,分配了⼋个MessageQueue。这⾥的MessageQueue就是⼀个典型的
具有FIFO(先进先出)特性的消息集合。这⼋个MessageQueue均匀的分布在了集群中的两个Broker服务上。每
个MesasgeQueue都记录了⼀个最⼩位点和最⼤位点。这⾥的位点代表每个MessageQueue上存储的消息的索
引,也称为offset(偏移量)。每⼀条新记录的消息,都按照当前最⼤位点往后分配⼀个新的位点。这个位点就记录了
这⼀条消息的存储位置。
从Dashboard就能看到,每⼀个MessageQueue,当前都记录了125条消息。也就是说,我们之前使⽤
Producer示例往RocketMQ中发送的⼀千条消息,就被均匀的分配到了这⼋个MessageQueue上。
这是,再回头来看之前⽇志中打印的SendResult的信息。⽇志中的MessageQueue就代表这⼀条消息存在哪个
队列上了。⽽queueOffset就表示这条消息记录在MessageQueue的哪个位置。
在这里插入图片描述
然后:我们尝试启动⼀个消费者来消费消息
我们同样可以使⽤tools.sh来启动⼀个消费者示例。

tools.sh org.apache.rocketmq.example.quickstart.Consumer

这个Consumer同样是RocketMQ下的lib/rocketmq-example-4.9.5.jar中提供的消费者示例。Consumer启动完
成后,我们可以在控制台看到很多类似这样的⽇志:

ConsumeMessageThread_3 Receive New Messages: [MessageExt [brokerName=broker-b,
queueId=0, storeSize=194, queueOffset=95, sysFlag=0, bornTimestamp=1666252677571,
bornHost=/192.168.232.128:38414, storeTimestamp=1666252678510,
storeHost=/192.168.232.130:10911, msgId=C0A8E88200002A9F0000000000B4ADD2,
commitLogOffset=11840978, bodyCRC=634652396, reconsumeTimes=0,
preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties=
{MIN_OFFSET=0, MAX_OFFSET=125, CONSUME_START_TIME=1666257428525,
UNIQ_KEY=7F000001426E28A418FC6545DDC302F9, CLUSTER=rocketmq-cluster, TAGS=TagA}, body=
[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81, 32, 55, 54, 49],
transactionId='null'}]]

这⾥⾯也打印出了⼀些我们刚刚熟悉的brokerName,queueId,queueOffset这些属性。其中queueOffset属
性就表示这⼀条消息在MessageQueue上的存储位点。通过记录每⼀个消息的Offset偏移量,RocketMQ就可以快
速的定位到这⼀条消息具体的存储位置,继⽽正确读取到消息的内容。
接下来,我们还是可以到DashBoard上印证⼀下消息消费的情况。
在DashBoard的“主题”⻚⾯,选择对应主题后的“CONSUMER管理”功能,就能看到消费者的消费情况。
在这里插入图片描述
从这⾥可以看到,刚才的Comsumer示例启动了⼀个叫做please_rename_unique_group_name_4的消费者
组。然后这个消费者从⼋个队列中都消费了数据。后⾯的代理者位点记录的是当前MessageQueue上记录的最⼤消
息偏移量。⽽消费者位点记录的是当前消费者组在MessageQueue上消费的最⼤消息偏移量。其中的差值就表示当
前消费者组没有处理完的消息。
并且,从这⾥还可以看出,RocketMQ记录消费者的消费进度时,都是以“订阅组”为单位的。我们也可以使⽤上
⼀章节的示例,⾃⼰另外定义⼀个新的消费者组来消费TopicTest上的消息。这时,RocketMQ就会单独记录新消费
者组的消费进度。⽽新的消费者组,也能消费到TopicTest下的所有消息。
接下来:我们就可以梳理出RocketMQ的消息记录⽅式
对之前的实验过程进⾏梳理,我们就能抽象出RocketMQ的消息模型。如下图所示:在这里插入图片描述
⽣产者和消费者都可以指定⼀个Topic发送消息或者拉取消息。⽽Topic是⼀个逻辑概念。Topic中的消息会分布
在后⾯多个MessageQueue当中。这些MessageQueue会分布到⼀个或者多个broker中。
在RocketMQ的这个消息模型当中,最为核⼼的就是Topic。对于客户端,Topic代表了⼀类有相同业务规则的消
息。对于Broker,Topic则代表了系统中⼀系列存储消息的资源。所以,RocketMQ对于Topic是需要做严格管理
的。如果任由客户端随意创建Topic,那么服务端的资源管理压⼒就会⾮常⼤。默认情况下,Topic都需要由管理员
在RocketMQ的服务端⼿动进⾏创建,然后才能给客户端使⽤的。⽽我们之前在broker.conf中⼿动添加的
autoCreateTopic=true,就是表示可以由客户端⾃⾏创建Topic。这种配置⽅式显然只适⽤于测试环境,在⽣产环
境不建议打开这个配置项。如果需要创建 Topic,可以交由运维⼈员提前创建 Topic。
⽽对于业务来说,最为重要的就是消息Message了。⽣产者发送到某⼀个Topic下的消息,最终会保存在Topic下
的某⼀个MessageQueue中。⽽消费者来消费消息时,RocketMQ会在Broker端给每个消费者组记录⼀个消息的消
费位点Offset。通过Offset控制每个消费者组的消息处理进度。这样,每⼀条消息,在⼀个消费者组当中只被处理
⼀次。

从逻辑层⾯来看,RocketMQ 的消息模型和Kafka的消息模型是很相似的。没错,早期 RocketMQ 就是借鉴
Kafka设计出来的。但是,在后续的发展过程肿,RocketMQ 在Kafka的基础上,做了⾮常⼤的调整。所以,
对于RocketMQ,你也不妨回顾下Kafka,与Kafka对⽐着进⾏学习。
例如,在Kafka当中,如果Topic过多,会造成消息吞吐量下降。但是在RocketMQ中,对Topic的⽀持已经得
到很⼤的加强。Topic过多⼏乎不会影响整体性能。RocketMQ是怎么设计的?另外,之前Kafka课程中也分
析过,Leader选举的过程中,Kafka优先保证服务可⽤性,⽽⼀定程度上牺牲了消息的安全性,那么 RocketMQ
是怎么做的呢?保留这些问题,后续我们⼀⼀解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值