说明:本文适合有一定RocketMQ基础(知道消费者、生产者、路由注册、路由发现等概念)的小伙伴阅读。文章有点长,中间有部分红字还有点啰嗦,并且为个人经验,请谨慎阅读。文章中如有理解错误或不足之处,还请小伙伴们指出,我们相互学习讨论。
本人踩的坑:
- 容器做挂载时,宿主机上要挂载的目录没有授权,导致容器向宿主机写日志、数据等被拒绝
- 做remotingServer服务的端口映射时,宿主机的端口和容器的端口不一致。(这个真的困扰了我很久)
下面将搭建一个这样的集群:
- 一共2台Linux,假设分别为Linux①号,Linux②号
- 2个nameserver(位于①上)
- 2对主从broker(第1对主从位于①上,第2对主从位于②上)
- RocketMQ控制台(位于①上)
其实集群怎么搭不重要(你想把所有东西搭在同一台Linux上都可以),主要是我想介绍我踩的两个坑,给大家分享经验和避雷。
1、搭建集群
1.1、拉取镜像
docker pull apache/rocketmq:5.3.0
1.2、🚀安装两个nameserver
凡是要挂载的目录中,如果有容器向宿主机写数据的情况,都需先创建,然后授权。
- 创建nameserver1容器:
mkdir -p /app/rocketmq/nameserver1/logs
chmod 777 -R /app/rocketmq/nameserver1/logs
docker run \
--name nameserver1 \
-p 9876:9876 \
-v /app/rocketmq/nameserver1/logs:/home/rocketmq/logs \
-e "MAX_POSSIBLE_HEAP=100000000" \
-e "JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m" \
-d \
apache/rocketmq:5.3.0 \
sh mqnamesrv
AI说nameserver容器内的数据目录是/home/rocketmq/store,但是我进入容器内部并未发现有此目录(即使在broker等都已注册的情况下),所以这里先暂不挂载数据目录。
- 创建nameserver2容器:
mkdir -p /app/rocketmq/nameserver2/logs
chmod 777 -R /app/rocketmq/nameserver2/logs
docker run \
--name nameserver2 \
-p 9877:9876 \
-v /app/rocketmq/nameserver2/logs:/home/rocketmq/logs \
-e "MAX_POSSIBLE_HEAP=100000000" \
-e "JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m" \
-d \
apache/rocketmq:5.3.0 \
sh mqnamesrv
1.3、🚀安装两对主从broker
1.3.1、🍀安装第1对主从
宿主机上,创建要挂载broker.conf配置文件,内容如下(brokerIP记得改成自己的):
# 集群名称
brokerClusterName = hangzhou1
# 主和从的名称需要保持一致
brokerName = broker-a
# 主从标识,0为主,其他大于0的为从,从只允许读,主可以读写
brokerId = 0
# 未消费的持久化消息清理时间点,默认凌晨4点
deleteWhen = 04
# 持久化消息保存周期(单位:小时)
fileReservedTime = 48
# brocker角色,主
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole = ASYNC_MASTER
# 刷盘方式:异步刷盘
flushDiskType = ASYNC_FLUSH
# broker自己的ip。将来服务注册到nameserver的ip就是这个。由于这里只写了一个ip,所以将来主从同步时,也会用这个ip。
brokerIP1 = 192.168.186.100
# 可以配置多个ip,示例:brokerIP1=xxx brokerIP2=xxx。配置了多个ip时,将来nameserver会自动选择用哪个ip进行与客户端通信,用哪个ip进行主从同步。
# 实际开发中通常配置两个ip,一个公网ip,一个内网ip。
# brokerIP2 = 192.168.186.100
# !!!请仔细阅读下面这2行注释!!!
# listenPort属性用于指定哪个端口作为remotingServer服务(这个服务就是用来连接客户端的)组件使用的端口,并且将来服务注册到nameserver的
# 端口号也是这个。这就意味着,你在创建容器时指定的端口映射中,宿主机必须和容器内部的端口一致。
listenPort = 10911
# nameserver的ip+端口。多个地址用分号隔开
namesrvAddr = 192.168.186.100:9876;192.168.186.100:9877
# 延迟消息等级时间。
# 这个设置也影响全局的消费重试机制的间隔时间,消费重试机制走的就是延迟消息,这里设置后,第一次重试是在是失败的10s后(从第三个开始,
# DefaultMQPushConsumerImpl源码中setDelayTimeLevel(3 + msg.getReconsumeTimes()),舍去了前两个),第二次是上次失败的30s
# 后,因为消费重试次数是16次(DefaultMQPushConsumerImpl源码默认是16次),而我们30s后面没有设置了,他后面的13次都会按30s间隔去重试。
messageDelayLevel = 1s 5s 10s 30s
# 在发送消息时,自动创建服务器不存在的topic时,默认创建的队列数,与Master节点保持一致或根据实际需求调整
defaultTopicQueueNums=4
# 是否允许Broker自动创建Topic,建议线下开启,线上关闭,与Master节点保持一致
autoCreateTopicEnable=true
# 是否允许Broker自动创建订阅组,建议线下开启,线上关闭,与Master节点保持一致
autoCreateSubscriptionGroup=true
# commitLog每个文件的大小默认1G,与Master节点保持一致
mapedFileSizeCommitLog=1073741824
# ConsumeQueue每个文件默认存30W条,根据业务情况调整,与Master节点保持一致
mapedFileSizeConsumeQueue=300000
# 设置所在机器磁盘占用超95%才报错
diskMaxUsedSpaceRatio = 95
🔖默认情况下,容器内这3个端口的作用:
10911端口:是remotingServer服务(用于客户端与Broker之间的通信)默认使用的监听端口。可通过broker.conf的listenPort属性进行修改。
10912端口:用于主从数据同步默认使用的端口。默认是10912,可通过broker.conf的haListenPort属性修改。
10909端口:是fastRemotingServer使用的监听端口。当vipChannelEnabled开启时,producer和consumer发送的消息会发送到fastRemotingServer进行快速通信。
🔖注意,下面创建容器时的端口映射很坑:
正常情况在端口映射时,容器某个服务的端口为8080,宿主机端口可以指定为任何可用的端口就可暴露容器中的该服务。
但是在创建RocketMQ的broker容器时,有一个服务比较特殊,那就是remotingServer服务用的端口。上方也介绍了,容器内remotingServer使用的端口是可以在broker.conf中通过listenPort属性指定的。假设你指定的是10911,而listenPort属性指定的端口号,会被直接和ip地址拼接为“ip:10911”,然后路由注册到nameserver,客户端做服务发现后,就会找“ip:10911”连接,这就限制我们在创建容器时,宿主机端口也指定为10911才行,否则客户端连不上。
所以,如果你在broker.conf中指定listenPort = 10911,那么创建容器时,端口映射就应该“-p 10911:10911”;而不能将宿主机端口指定成其它的(如:“-p 10811:10911”是不行的)。
这个我觉得很坑,因为它不允许你在暴露容器内的remotingServer服务时可以自由地指定宿主机端口。
创建主节点容器(记得将nameserver的ip和端口改成自己的):
mkdir -p /app/rocketmq/broker-a-master/data
mkdir -p /app/rocketmq/broker-a-master/logs
chmod 777 -R /app/rocketmq/broker-a-master/data
chmod 777 -R /app/rocketmq/broker-a-master/logs
docker run \
--name broker-a-master \
-p 10911:10911 \
-p 10912:10912 \
-p 10909:10909 \
-v /app/rocketmq/broker-a-master/data:/home/rocketmq/store \
-v /app/rocketmq/broker-a-master/logs:/home/rocketmq/logs \
-v /app/rocketmq/broker-a-master/conf/broker.conf:/home/rocketmq/rocketmq-5.3.0/conf/broker.conf \
-e "NAMESRV_ADDR=192.168.186.100:9876;192.168.186.100:9877" \
-e "JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m" \
-e "MAX_POSSIBLE_HEAP=200000000" \
-d \
apache/rocketmq:5.3.0 \
sh mqbroker -c /home/rocketmq/rocketmq-5.3.0/conf/broker.conf
端口映射说明:
-p 10911:10911 暴露容器中的remotingServer服务,监听客户端的连接。
-p 10912:10912 暴露容器中的主从同步服务,监听数据同步请求。
-p 10909:10909 暴露容器中的fastRemotingServer服务。
- 从节点要挂载的的broker.conf配置文件(brokerIP记得改成自己的):
# 集群名称
brokerClusterName = hangzhou1
# 主和从的名称需要保持一致
brokerName = broker-a
# 主从标识,0为主,其他大于0的为从,从只允许读,主可以读写
brokerId = 1
# 未消费的持久化消息清理时间点,默认凌晨4点
deleteWhen = 04
# 持久化消息保存周期(单位:小时)
fileReservedTime = 48
# brocker角色,从
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole = SLAVE
# 刷盘方式:异步刷盘
flushDiskType = ASYNC_FLUSH
# broker自己的ip。将来服务注册到nameserver的ip就是这个。由于这里只写了一个ip,所以将来主从同步时,也会用这个ip。
brokerIP1 = 192.168.186.100
# 可以配置多个ip,示例:brokerIP1=xxx brokerIP2=xxx。配置了多个ip时,将来nameserver会自动选择用哪个ip进行与客户端通信,用哪个ip进行主从同步。
# 实际开发中通常配置两个ip,一个公网ip,一个内网ip。
# brokerIP2 = 192.168.186.100
# !!!请仔细阅读下面这2行注释!!!
# listenPort属性用于指定哪个端口作为remotingServer服务(这个服务就是用来连接客户端的)组件使用的端口,并且将来服务注册到nameserver的
# 端口号也是这个。这就意味着,你在创建容器时指定的端口映射中,宿主机必须和容器内部的端口一致。
listenPort = 10811
# nameserver的ip+端口。多个地址用分号隔开
namesrvAddr = 192.168.186.100:9876;192.168.186.100:9877
# 延迟消息等级时间。
# 这个设置也影响全局的消费重试机制的间隔时间,消费重试机制走的就是延迟消息,这里设置后,第一次重试是在是失败的10s后(从第三个开始,
# DefaultMQPushConsumerImpl源码中setDelayTimeLevel(3 + msg.getReconsumeTimes()),舍去了前两个),第二次是上次失败的30s
# 后,因为消费重试次数是16次(DefaultMQPushConsumerImpl源码默认是16次),而我们30s后面没有设置了,他后面的13次都会按30s间隔去重试。
messageDelayLevel = 1s 5s 10s 30s
# 在发送消息时,自动创建服务器不存在的topic时,默认创建的队列数,与Master节点保持一致或根据实际需求调整
defaultTopicQueueNums=4
# 是否允许Broker自动创建Topic,建议线下开启,线上关闭,与Master节点保持一致
autoCreateTopicEnable=true
# 是否允许Broker自动创建订阅组,建议线下开启,线上关闭,与Master节点保持一致
autoCreateSubscriptionGroup=true
# commitLog每个文件的大小默认1G,与Master节点保持一致
mapedFileSizeCommitLog=1073741824
# ConsumeQueue每个文件默认存30W条,根据业务情况调整,与Master节点保持一致
mapedFileSizeConsumeQueue=300000
# 设置所在机器磁盘占用超95%才报错
diskMaxUsedSpaceRatio = 95
创建从节点容器(记得将nameserver的ip和端口改成自己的):
mkdir -p /app/rocketmq/broker-a-slave/data
mkdir -p /app/rocketmq/broker-a-slave/logs
chmod 777 -R /app/rocketmq/broker-a-slave/data
chmod 777 -R /app/rocketmq/broker-a-slave/logs
docker run \
--name broker-a-slave \
-p 10811:10811 \
-p 10812:10912 \
-p 10809:10909 \
-v /app/rocketmq/broker-a-slave/data:/home/rocketmq/store \
-v /app/rocketmq/broker-a-slave/logs:/home/rocketmq/logs \
-v /app/rocketmq/broker-a-slave/conf/broker.conf:/home/rocketmq/rocketmq-5.3.0/conf/broker.conf \
-e "NAMESRV_ADDR=192.168.186.100:9876;192.168.186.100:9877" \
-e "JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m" \
-e "MAX_POSSIBLE_HEAP=200000000" \
-d \
apache/rocketmq:5.3.0 \
sh mqbroker -c /home/rocketmq/rocketmq-5.3.0/conf/broker.conf
1.3.2、🍀安装第2对主从
现在到第2台Linux上操作
- 主节点的broker.conf配置文件(brokerIP记得改成自己的):
# 集群名称
brokerClusterName = hangzhou1
# 主和从的名称需要保持一致
brokerName = broker-b
# 主从标识,0为主,其他大于0的为从,从只允许读,主可以读写
brokerId = 0
# 未消费的持久化消息清理时间点,默认凌晨4点
deleteWhen = 04
# 持久化消息保存周期(单位:小时)
fileReservedTime = 48
# brocker角色,主
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole = ASYNC_MASTER
# 刷盘方式:异步刷盘
flushDiskType = ASYNC_FLUSH
# broker自己的ip。将来服务注册到nameserver的ip就是这个。由于这里只写了一个ip,所以将来主从同步时,也会用这个ip。
brokerIP1 = 192.168.186.101
# 可以配置多个ip,示例:brokerIP1=xxx brokerIP2=xxx。配置了多个ip时,将来nameserver会自动选择用哪个ip进行与客户端通信,用哪个ip进行主从同步。
# 实际开发中通常配置两个ip,一个公网ip,一个内网ip。
# brokerIP2 = 192.168.186.100
# !!!请仔细阅读下面这2行注释!!!
# listenPort属性用于指定哪个端口作为remotingServer服务(这个服务就是用来连接客户端的)组件使用的端口,并且将来服务注册到nameserver的
# 端口号也是这个。这就意味着,你在创建容器时指定的端口映射中,宿主机必须和容器内部的端口一致。
listenPort = 10711
# nameserver的ip+端口。多个地址用分号隔开
namesrvAddr = 192.168.186.100:9876;192.168.186.100:9877
# 延迟消息等级时间。
# 这个设置也影响全局的消费重试机制的间隔时间,消费重试机制走的就是延迟消息,这里设置后,第一次重试是在是失败的10s后(从第三个开始,
# DefaultMQPushConsumerImpl源码中setDelayTimeLevel(3 + msg.getReconsumeTimes()),舍去了前两个),第二次是上次失败的30s
# 后,因为消费重试次数是16次(DefaultMQPushConsumerImpl源码默认是16次),而我们30s后面没有设置了,他后面的13次都会按30s间隔去重试。
messageDelayLevel = 1s 5s 10s 30s
# 在发送消息时,自动创建服务器不存在的topic时,默认创建的队列数,与Master节点保持一致或根据实际需求调整
defaultTopicQueueNums=4
# 是否允许Broker自动创建Topic,建议线下开启,线上关闭,与Master节点保持一致
autoCreateTopicEnable=true
# 是否允许Broker自动创建订阅组,建议线下开启,线上关闭,与Master节点保持一致
autoCreateSubscriptionGroup=true
# commitLog每个文件的大小默认1G,与Master节点保持一致
mapedFileSizeCommitLog=1073741824
# ConsumeQueue每个文件默认存30W条,根据业务情况调整,与Master节点保持一致
mapedFileSizeConsumeQueue=300000
# 设置所在机器磁盘占用超95%才报错
diskMaxUsedSpaceRatio = 95
创建主节点容器(记得将nameserver的ip和端口改成自己的):
mkdir -p /app/rocketmq/broker-b-master/data
mkdir -p /app/rocketmq/broker-b-master/logs
chmod 777 -R /app/rocketmq/broker-b-master/data
chmod 777 -R /app/rocketmq/broker-b-master/logs
docker run \
--name broker-b-master \
-p 10711:10711 \
-p 10712:10912 \
-p 10709:10909 \
-v /app/rocketmq/broker-b-master/data:/home/rocketmq/store \
-v /app/rocketmq/broker-b-master/logs:/home/rocketmq/logs \
-v /app/rocketmq/broker-b-master/conf/broker.conf:/home/rocketmq/rocketmq-5.3.0/conf/broker.conf \
-e "NAMESRV_ADDR=192.168.186.100:9876;192.168.186.100:9877" \
-e "JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m" \
-e "MAX_POSSIBLE_HEAP=200000000" \
-d \
apache/rocketmq:5.3.0 \
sh mqbroker -c /home/rocketmq/rocketmq-5.3.0/conf/broker.conf
- 从节点的broker.conf配置文件(brokerIP记得改成自己的):
# 集群名称
brokerClusterName = hangzhou1
# 主和从的名称需要保持一致
brokerName = broker-b
# 主从标识,0为主,其他大于0的为从,从只允许读,主可以读写
brokerId = 1
# 未消费的持久化消息清理时间点,默认凌晨4点
deleteWhen = 04
# 持久化消息保存周期(单位:小时)
fileReservedTime = 48
# brocker角色,从
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole = SLAVE
# 刷盘方式:异步刷盘
flushDiskType = ASYNC_FLUSH
# broker自己的ip。将来服务注册到nameserver的ip就是这个。由于这里只写了一个ip,所以将来主从同步时,也会用这个ip。
brokerIP1 = 192.168.186.101
# 可以配置多个ip,示例:brokerIP1=xxx brokerIP2=xxx。配置了多个ip时,将来nameserver会自动选择用哪个ip进行与客户端通信,用哪个ip进行主从同步。
# 实际开发中通常配置两个ip,一个公网ip,一个内网ip。
# brokerIP2 = 192.168.186.100
# !!!请仔细阅读下面这2行注释!!!
# listenPort属性用于指定哪个端口作为remotingServer服务(这个服务就是用来连接客户端的)组件使用的端口,并且将来服务注册到nameserver的
# 端口号也是这个。这就意味着,你在创建容器时指定的端口映射中,宿主机必须和容器内部的端口一致。
listenPort = 10611
# nameserver的ip+端口。多个地址用分号隔开
namesrvAddr = 192.168.186.100:9876;192.168.186.100:9877
# 延迟消息等级时间。
# 这个设置也影响全局的消费重试机制的间隔时间,消费重试机制走的就是延迟消息,这里设置后,第一次重试是在是失败的10s后(从第三个开始,
# DefaultMQPushConsumerImpl源码中setDelayTimeLevel(3 + msg.getReconsumeTimes()),舍去了前两个),第二次是上次失败的30s
# 后,因为消费重试次数是16次(DefaultMQPushConsumerImpl源码默认是16次),而我们30s后面没有设置了,他后面的13次都会按30s间隔去重试。
messageDelayLevel = 1s 5s 10s 30s
# 在发送消息时,自动创建服务器不存在的topic时,默认创建的队列数,与Master节点保持一致或根据实际需求调整
defaultTopicQueueNums=4
# 是否允许Broker自动创建Topic,建议线下开启,线上关闭,与Master节点保持一致
autoCreateTopicEnable=true
# 是否允许Broker自动创建订阅组,建议线下开启,线上关闭,与Master节点保持一致
autoCreateSubscriptionGroup=true
# commitLog每个文件的大小默认1G,与Master节点保持一致
mapedFileSizeCommitLog=1073741824
# ConsumeQueue每个文件默认存30W条,根据业务情况调整,与Master节点保持一致
mapedFileSizeConsumeQueue=300000
# 设置所在机器磁盘占用超95%才报错
diskMaxUsedSpaceRatio = 95
创建从节点容器(记得将nameserver的ip和端口改成自己的):
mkdir -p /app/rocketmq/broker-b-slave/data
mkdir -p /app/rocketmq/broker-b-slave/logs
chmod 777 -R /app/rocketmq/broker-b-slave/data
chmod 777 -R /app/rocketmq/broker-b-slave/logs
docker run \
--name broker-b-slave \
-p 10611:10611 \
-p 10612:10912 \
-p 10609:10909 \
-v /app/rocketmq/broker-b-slave/data:/home/rocketmq/store \
-v /app/rocketmq/broker-b-slave/logs:/home/rocketmq/logs \
-v /app/rocketmq/broker-b-slave/conf/broker.conf:/home/rocketmq/rocketmq-5.3.0/conf/broker.conf \
-e "NAMESRV_ADDR=192.168.186.100:9876;192.168.186.100:9877" \
-e "JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m" \
-e "MAX_POSSIBLE_HEAP=200000000" \
-d \
apache/rocketmq:5.3.0 \
sh mqbroker -c /home/rocketmq/rocketmq-5.3.0/conf/broker.conf
2、安装控制台工具
现在到第1台Linux上操作
rocket本身没有提供可视化管理工具,只提供了个mqadmin(命令行)。所以我们安装第三方的可视化管理工具。工具的官方地址:
https://github.com/apache/rocketmq-externals/tree/rocketmq-console-1.0.0/rocketmq-console
拉取镜像:
docker pull styletang/rocketmq-console-ng
创建容器(记得将nameserver的ip和端口改成自己的):
docker run \
--name rocket-console \
-e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.186.100:9876;192.168.186.100:9877 -Dcom.rocketmq.sendMessageWithVIPChannel=false" \
-p 9880:8080 \
-d styletang/rocketmq-console-ng
ip+端口
访问控制台,nameserver信息如下图:
broker信息如下图:
至此,搭建完毕。