前言
本专题整理了RocketMQ单机部署、集群部署、Dledger高可用集群部署的详细步骤。配合系统参数调优的讲解,能够帮助大家从多个维度去理解RocketMQ的工作原理。
基本介绍
首先简单介绍一下今天的主角:
RocketMQ是阿里巴巴开源的一个消息中间件。早期阿里使用ActiveMQ,但是,当消息开始逐渐增多后,ActiveMQ的IO性能很快达到了瓶颈。
于是,阿里开始关注Kafka。但是Kafka是针对日志收集场景设计的,并发性能并不是很理想。尤其当他的Topic过多时,由于Partition文件也会过多,会严重影响IO性能。
于是阿里才决定自研中间件,最早叫做MetaMQ,后来改名成为RocketMQ。
一开始希望解决的最大问题就是多Topic下的IO性能压力。在阿里内部的不断改进下,RocketMQ开始体现出一些不一样的优势。
RocketMQ的消息吞吐量虽然依然不如Kafka,但是却比RabbitMQ高很多。在阿里内部,RocketMQ集群每天处理的请求数超过5万亿次,支持的核心应用超过3000个。
除了支持Java语言。在RocketMQ的社区,也正在开发GO,Python,Nodejs等语言的客户端。
下载地址
RocketMQ的官网地址: http://rocketmq.apache.org github地址是 https://github.com/apache/rocketmq
找到想要安装的版本,点击下载即可
在本专题中,我们选择的版本为4.9.1版本。
工作原理
RocketMQ由以下这几个组件组成:
- Producer:消息的发送者,通常是业务系统中的一个功能模块。举例理解:寄件人。
- Consumer:消息接收者,通常也是业务系统中的一个功能模块。举例理解:收件人。
- Broker:实际处理消息存储、转发等服务的核心组件。举例理解:邮局。
- NameServer:管理Broker及路由信息。举例理解:邮政管理系统。
部署前准备
不管是单点还是集群部署,在安装RocketMQ之前都需要确保服务器环境如下:
准备一台CentOS7的Linux机器,快速把RocketMQ给运行起来。我使用的Linux版本如下:
[oper@worker1 jdk1.8]$ uname -a
Linux worker1 3.10.0-1127.el7.x86_64 #1 SMP Tue Mar 31 23:36:51 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
创建一个操作用户用来运行自己的程序,与root用户区分开。使用root用户创建一个oper用户,并给他创建一个工作目录,并设置密码。
[root@worker1 ~]# useradd oper
[root@worker1 ~]# passwd oper
[root@worker1 ~]# mkdir /app
[root@worker1 ~]# chown oper:oper /app
运行RocketMQ需要先安装JDK。由oper用户解压到/app/jdk1.8
目录下。
[oper@worker1 tools]$ tar -zxvf jdk-8u171-linux-x64.tar.gz
[oper@worker1 tools]$ mv jdk1.8.0_171/ /app/jdk1.8
配置JAVA环境变量。使用 vi ~/.bash_profile
编辑文件,在下面加入以下内容:
export JAVA_HOME=/app/jdk1.8/
PATH=$JAVA_HOME/bin:$PATH:$HOME/.local/bin:$HOME/bin
export PATH
编辑完成后,执行 source ~/.bash_profile
让环境变量生效。输入 java -version
能查看到以下内容表明JDK安装成功了。
[oper@worker1 ~]$ java -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
把下载的rocketmq-all-4.9.1-bin-release.zip
在本地完成解压,并上传到/app/rocketmq
目录。完成后,把rocketmq的bin目录也配置到环境变量当中。
vi ~/.bash_profile
,加入以下内容,并执行 source ~/.bash_profile
让环境变量生效:
export JAVA_HOME=/app/jdk1.8/
export ROCKETMQ_HOME=/app/rocketmq/rocketmq-all-4.9.1-bin-release
PATH=$ROCKETMQ_HOME/bin:$JAVA_HOME/bin:$PATH:$HOME/.local/bin:$HOME/bin
export PATH
这个ROCKETMQ_HOME的环境变量是必须要单独配置的,如果不配置的话,启动NameSever和Broker都会报错。
这个环境变量的作用是用来加载$ROCKETMQ_HOME/conf下的除broker.conf以外的几个配置文件。
所以实际情况中,可以不按这个配置,但是一定要能找到配置文件。
单点部署
NameServer启动
要启动RocketMQ服务,需要先启动NameServer。
RocketMQ默认预设的JVM内存是4G,这是RocketMQ给我们的最佳配置。但是通常我们用虚拟机的话都是不够4G内存的,所以需要调整下JVM内存大小。
修改的方式是直接修改runserver.sh
。 用vi runserver.sh
编辑这个脚本,在脚本中找到这一行调整内存大小为512M。
JAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx512m -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
在$ROCKETMQ_HOME/bin
目录下有个mqadminsrv
。直接执行这个脚本就可以启动RocketMQ的NameServer服务。
nohup bin/mqnamesrv &
启动完成后,在nohup.out里看到这一条关键日志就是启动成功了。使用jps指令可以看到有一个NamesrvStartup进程。
Java HotSpot(TM) 64-Bit Server VM warning: Using the DefNew young collectorwith 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
Broker服务搭建
启动Broker的脚本是runbroker.sh
。默认预设内存是8G,如果服务器内存不够,同样需要调整下JVM内存。
1、执行 vi runbroker.sh
,找到这一行,进行内存调整。
JAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx512m"
2、然后我们需要编辑 $ROCKETMQ_HOME/conf/broker.conf
,在最下面加入一个配置:
autoCreateTopicEnable=true
使用RocketMQ进行发消息时,必须要指定topic
一般在开发测试环境中会使用默认设置 autoCreateTopicEnable = true
但是这样就会导致topic的设置不容易规范管理,没有统一的审核
生产环境中会讲将该参数改为:autoCreateTopicEnable = false
需要增加topic时就需要在web管理界面上添加即可
3、启动完成后,同样是检查nohup.out日志,有这一条关键日志就标识启动成功了。 使用jps指令可以看到一个BrokerStartup进程。
The broker[worker1, 192.168.232.128:10911] boot success. serializeType=JSON
在观察runserver.sh和runbroker.sh时,我们还可以查看到其他的JVM执行参数。
这些参数都可以进行定制。
我们观察到一个比较有意思的地方。
NameServer使用的是CMS垃圾回收器。
而Broker使用的是G1。
测试客户端
在RocketMQ的安装包中,提供了一个tools.sh工具可以用来在命令行快速验证RocketMQ服务。我们进入RocketMQ的安装目录:
首先需要配置一个环境变量$NAMESRV_ADDR
指向我们启动的NameServer服务。
export NAMESRV_ADDR='localhost:9876'
然后启动消息生产者发送消息:默认会发1000条消息
bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
我们可以看到发送消息的日志:
.....
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
这日志中,上面部分就是我们发送的消息的内容。后面两句标识消息生产者正常关闭。
然后启动消息消费者接收消息:
bin/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'}]]
日志中MessageExt后的整个内容就是一条完整的RocketMQ消息。我们要对这个消息的结构有个大概的了解,后面会对这个消息进行深入的理解。
其中比较关键的属性有:brokerName,queueId,msgId,topic,cluster,tags,body,transactionId。
注意:这个Consume指令并不会结束,他会继续挂起,等待消费其他的消息。我们可以使用CTRL+C停止该进程。
关闭RocketMQ服务
要关闭RocketMQ服务可以通过mqshutdown脚本直接关闭
# 1.关闭NameServer
sh bin/mqshutdown namesrv
# 2.关闭Broker
sh bin/mqshutdown broker
Master/Slave集群部署
集群模式介绍
1、多主集群
一个集群无 Slave,全是 Master,例如 2 个 Master 或者 3 个 Master。
- 优点:配置简单,单个Master宕机或重启维护对应用无影响。
即使机器宕机不可恢复情况下,也有其他主节点支持写入操作。
消息也不会丢(异步刷盘丢失少量消息,同步刷盘一条不丢),性能最高。 - 缺点:单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅。
消息实时性会受到受到影响。
2、双主双从同步
每个 Master 配置一个 Slave,有多对 Master-Slave,HA 采用同步双写方式。
主节点收到消息之后不立马返回给用户,会等从节点复制成功之后再返回发送成功的消息给用户。主备都写成功时,向应用返回成功。
- 优点:数据与服务都无单点,Master 宕机情况下,消息无延迟,服务可用性与数据可用性、安全性都非常高。
- 缺点:相比异步复制,性能比异步复制模式略低。客户端接受服务器响应时间更长。可用于消息安全高的场景。
3、双主双从异步
每个 Master 配置一个 Slave,有多对 Master-Slave,HA 采用异步复制方式
主节点收到消息之后立马就返回给用户,然后再开一个线程与从节点进行复制。主备有短暂毫秒级消息延迟。
- 优点:相比同步双写,客户端能更快的接收到服务器的消息,效率较高。
- 缺点:Master 宕机,磁盘损坏情况,会丢失少量消息,可用于允许消息部分丢失的场景。
服务器准备
为了便于观察,这次搭建一个2主2从异步刷盘的集群
准备三台虚拟机,硬盘空间建议大于4G。配置机器名。
下文的描述中,会更多的会关注机器名,而不会太多关注IP地址。
vi /etc/hosts
192.168.232.128 worker1
192.168.232.129 worker2
192.168.232.130 worker3
配置主从集群
把RocketMQ的安装包上传到服务器,预备设计的集群情况如下:
机器名 | nemaeServer节点部署 | broker节点部署 |
worker1 | nameserver | |
worker2 | nameserver | broker-a, broker-b-s |
worker3 | nameserver | broker-b, broker-a-s |
配置第一组broker-a
在worker2上先配置borker-a的master节点。先配置config/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
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=10911
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/app/rocketmq/store
#commitLog 存储路径
storePathCommitLog=/app/rocketmq/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/app/rocketmq/store/consumequeue
#消息索引存储路径
storePathIndex=/app/rocketmq/store/index
#checkpoint 文件存储路径
storeCheckpoint=/app/rocketmq/store/checkpoint
#abort 文件存储路径
abortFile=/app/rocketmq/store/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=ASYNC_MASTER
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_FLUSH
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
该节点对应的从节点在worker3上。修改config/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
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=11011
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/app/rocketmq/storeSlave
#commitLog 存储路径
storePathCommitLog=/app/rocketmq/storeSlave/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/app/rocketmq/storeSlave/consumequeue
#消息索引存储路径
storePathIndex=/app/rocketmq/storeSlave/index
#checkpoint 文件存储路径
storeCheckpoint=/app/rocketmq/storeSlave/checkpoint
#abort 文件存储路径
abortFile=/app/rocketmq/storeSlave/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SLAVE
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_FLUSH
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
配置第二组broker-b
这一组broker的主节点在worker3上,需要配置worker3上的config/2m-2s-async/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
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=10911
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/app/rocketmq/store
#commitLog 存储路径
storePathCommitLog=/app/rocketmq/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/app/rocketmq/store/consumequeue
#消息索引存储路径
storePathIndex=/app/rocketmq/store/index
#checkpoint 文件存储路径
storeCheckpoint=/app/rocketmq/store/checkpoint
#abort 文件存储路径
abortFile=/app/rocketmq/store/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=ASYNC_MASTER
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_FLUSH
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
然后他对应的slave在worker2上,修改work2上的 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
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=11011
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/app/rocketmq/storeSlave
#commitLog 存储路径
storePathCommitLog=/app/rocketmq/storeSlave/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/app/rocketmq/storeSlave/consumequeue
#消息索引存储路径
storePathIndex=/app/rocketmq/storeSlave/index
#checkpoint 文件存储路径
storeCheckpoint=/app/rocketmq/storeSlave/checkpoint
#abort 文件存储路径
abortFile=/app/rocketmq/storeSlave/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SLAVE
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_FLUSH
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
这样2主2从的集群配置基本就完成了。搭建过程中需要注意的配置项:
1、同一机器上两个实例的store目录不能相同,否则会报错 Lock failed,MQ already started
2、同一机器上两个实例的listenPort也不能相同。否则会报端口占用的错。nameserver不需要进行配置,直接启动就行。这也看出nameserver是无状态的。
3、如果是多网卡的机器,比如云服务器,那么需要在broker.conf中增加brokerIP属性,指定所在机器的外网网卡地址。
启动RocketMQ
启动就比较简单了,直接调用bin目录下的脚本就行。只是启动之前要注意看下他们的JVM内存配置,默认的配置都比较高。
1、启动NameServer
与单点部署的步骤相同,修改三个节点上的bin/runserver.sh,调整里面的jvm内存配置。
直接在三个节点上启动nameServer。
2、启动Broker
启动broker是使用的mqbroker指令,只是注意启动broker时需要通过-c 指定对应的配置文件。
在worker2上启动broker-a的master节点
和broker-b的slave节点
nohup ./mqbroker -c ../conf/2m-2s-async/broker-a.properties &
nohup ./mqbroker -c ../conf/2m-2s-async/broker-b-s.properties &
在work3上启动broker-b的master节点
和broker-a的slave节点
nohup ./mqbroker -c ../conf/2m-2s-async/broker-b.properties &
nohup ./mqbroker -c ../conf/2m-2s-async/broker-a-s.properties &
启动slave时,如果遇到报错 Lock failed,MQ already started ,那是因为有多个实例共用了同一个storePathRootDir造成的,这时就需要调整store的路径。
3、启动状态检查
使用jps指令,能看到一个NameSrvStartup进程和两个BrokerStartup进程。nohup.out中也有启动成功的日志。
对应的日志文件:
# 查看nameServer日志
tail -500f ~/logs/rocketmqlogs/namesrv.log
# 查看broker日志
tail -500f ~/logs/rocketmqlogs/broker.log
4、测试mqadmin管理工具
RocketMQ源码中并没有提供管理控制台,只提供了一个mqadmin指令来管理RocketMQ。指令的位置在bin目录下。直接使用该指令就会列出所有支持的命令。
使用方式都是 mqadmin {command} {args}
。 如果有某个指令不会使用,可以使用 mqadmin help {command} 指令查看帮助。
5、命令行快速验证
RocketMQ提供了一个tools.sh工具可以用来在命令行快速验证RocketMQ服务。例如,在worker2机器上进入RocketMQ的安装目录:
发送消息:默认会发1000条消息
bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
接收消息:
bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
注意,这是官方提供的Demo,但是官方的源码中,这两个类都是没有指定nameServer
的,所以运行会有点问题。
要指定NameServer
地址,可以配置一个环境变量NAMESRV_ADDR
,这样默认会读取这个NameServer
地址。可以配到.bash_profile
里或者直接临时指定。
export NAMESRV_ADDR='worker1:9876;worker2:9876;worker3:9876'
然后就可以正常执行了。
这个tooles.sh实际上是封装了一个简单的运行RocketMQ的环境,上面指令中指定的Java类,都在lib/rocketmq-example-4.9.1.jar
包中。未来如果自己有一些客户端示例,也可以打成jar包放到这个lib目录下,通过tools.sh运行。
搭建管理控制台
RocketMQ源代码中并没有提供控制台,但是有一个Rocket的社区扩展项目中提供了一个控制台.
地址: https://github.com/apache/rocketmq-dashboard
下载下来后,解压并进入对应的目录,使用maven进行编译
mvn clean package -Dmaven.test.skip=true
编译完成后,获取target
下的jar包,就可以直接执行。
但是这个时候要注意,在这个项目的application.yml
中需要指定nameserver
的地址。
默认这个属性是指向本地。如果配置为空,会读取环境变量NAMESRV_ADDR
。
那我们可以在jar包的当前目录下增加一个application.yml
文件,覆盖jar包中默认的一个属性:
rocketmq:
config:
namesrvAddrs:
- worker1:9876
- worker2:9876
- worker3:9876
其他更多配置信息可以参考dashboard源码中的配置文件。
然后执行:
java -jar rocketmq-dashboard-1.0.1-SNAPSHOT.jar
启动完成后,可以访问 http://192.168.232.128:8080 看到管理页面
Dledger高可用集群部署
主从切换
Dledger 作为一个轻量级的 Java Library,它的作用就是将 Raft 有关于算法方面的内容全部抽象掉,开发人员只需要关心业务即可。
在 RocketMQ 4.5 版本之前,RocketMQ 只有 Master/Slave 一种部署方式:
一组 broker 中有一个 Master ,有零到多个Slave,Slave 通过同步复制或异步复制的方式去同步 Master 数据。Master/Slave 部署模式,提供了一定的高可用性。
但这样都 Master/Slave 部署模式,有一定缺陷:
1、比如故障转移方面,如果主节点挂了,还需要人为手动进行重启或者切换,无法自动将一个从节点转换为主节点,缺乏容灾功能。因此,我们希望能有一个新的多副本架构,去解决这个问题。
2、如果slave挂了,对集群的影响不大,因为slave只是做数据备份的。但是影响也是会有的,例如:当消费者要拉取的数据量比较大时,RocketMQ为了保证Master节点的性能,只会让Master节点返回一小部分数据,而让其他部分的数据从slave节点去拉取。
此外,由于Dleger会有他自己的CommitLog机制,也就是说,使用主从集群累计下来的消息,是无法转移到Dleger集群中的,这一点需要格外注意。
源码分析 RocketMQ DLedger 多副本可以参考:
源码分析 RocketMQ DLedger 多副本即主从切换实现原理
搭建方法
为了节约篇幅,我会基于前一章节:Master/Slave集群部署 的基础上进行说明,默认与之相同的集群环境下进行安装。
首先:我们同样是需要修改runserver.sh
和runbroker.sh
,对JVM内存进行定制,准备好主从配置文件。
然后:我们需要修改conf/dleger
下的配置文件。 跟dleger相关的几个配置项如下:
name | 含义 | 举例 |
enableDLegerCommitLog | 是否启动 DLedger | true |
dLegerGroup | DLedger Raft Group 的名字,建议和 brokerName 保持一致 | RaftNode00 |
dLegerPeers | DLedger Group 内各节点的端口信息,同一个 Group内的各个节点配置必须要保证一致 | n0-127.0.0.1:40911;n1-127.0.0.1:40912;n2-127.0.0.1:40913 |
dLegerSelfId | 节点 id, 必须属于dLegerPeers 中的一个;同Group 内各个节点要唯一 | n0 |
sendMessageThreadPoolNums | 发送线程个数,建议配置成Cpu 核数 | 16 |
配置完后,启动时同样是使用 nohup bin/mqbroker -c $conf_name &
的方式指定实例文件。
在bin/dleger下有个fast-try.sh
,这个脚本是在本地启动三个RocketMQ实例,搭建一个高可用的集群,读取的就是conf/dleger下的broker-n0.conf
,broker-n1.conf
和broker-n2.conf
。
使用这个脚本同样要注意定制下JVM内存,他给每个实例默认定制的是1G内存,虚拟机肯定是不够的。
这种单机三实例的集群搭建完成后,可以使用 bin/mqadmin clusterList -n worker1.conf
的方式查看集群状态。
系统参数调优
到这里,我们的整个RocketMQ的服务就搭建完成了。
但是在实际使用时,由于RocketMQ的吞吐量、性能都很高,如果发挥RocketMQ的高性能,还需要对RocketMQ以及服务器的性能进行调优。
1、RocketMQ的JVM内存大小
在之前的部署过程中,我们不止一次的修改过:
1、runserver.sh中定制 nameserver
的内存大小。
2、runbroker.sh中定制broker
的内存大小。
这些默认的配置可以认为都是经过检验的最优化配置,但是在实际情况中都还需要根据服务器的实际情况进行调整。
这里以runbroker.sh中对G1GC的配置举例,在runbroker.sh中的关键配置:
JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XXInitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"
JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:${GC_LOG_DIR}/rmq_broker_gc_%p_%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"
JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
1、
-XX:+UseG1GC
:使用G1垃圾回收器
2、-XX:G1HeapRegionSize=16m
:将G1的region块大小设为16M
3、-XX:G1ReservePercent
:在G1的老年代中预留25%空闲内存,这个默认值是10%,RocketMQ把这个参数调大了。
4、-XX:InitiatingHeapOccupancyPercent=30
:当堆内存的使用率达到30%之后就会启动G1垃圾回收器尝试回收垃圾,默认值是45%,RocketMQ把这个参数调小了,也就是提高了GC的频率,但是避免了垃圾对象过多,一次垃圾回收时间太长的问题。
然后,后面定制了GC的日志文件,确定GC日志文件的地址、打印的内容以及控制每个日志文件的大小为30M并且只保留5个文件。这些在进行性能检验时,是相当重要的参考内容。
2、RocketMQ其他核心参数
例如在conf/dleger/broker-n0.conf中有一个参数:sendMessageThreadPoolNums=16
。
这一个参数是表明RocketMQ内部用来发送消息的线程池的线程数量是16个,其实这个参数可以根据机器的CPU核心数进行适当调整,例如如果你的机器核心数超过16个,就可以把这个参数适当调大。
3、Linux内核参数定制
我们在部署RocketMQ的时候,还需要对Linux内核参数进行一定的定制。例如:
1、
ulimit
:需要进行大量的网络通信和磁盘IO。
2、vm.extra_free_kbytes
:告诉VM在后台回收(kswapd)启动的阈值与直接回收(通过分配进程)的阈值之间保留额外的可用内存。RocketMQ使用此参数来避免内存分配中的长延迟。(与具体内核版本相关)
3、vm.min_free_kbytes
:如果将其设置为低于1024KB,将会巧妙的将系统破坏,并且系统在高负载下容易出现死锁。
4、vm.max_map_count
:限制一个进程可能具有的最大内存映射区域数。RocketMQ将使用mmap加载CommitLog和ConsumeQueue,因此建议将为此参数设置较大的值。
5、vm.swappiness
,定义内核交换内存页面的积极程度。较高的值会增加攻击性,较低的值会减少交换量。建议将值设置为10来避免交换延迟。
6、File descriptor limits
:RocketMQ需要为文件(CommitLog和ConsumeQueue)和网络连接打开文件描述符。我们建议设置文件描述符的值为655350。
这些Linux参数在CentOS7中的配置文件都在 /proc/sys/vm目录下。
此外,RocketMQ的bin目录下有个os.sh里面设置了RocketMQ建议的系统内核参数,可以根据情况进行调整。