最近在学习RocketMQ,第一步就是安装部署并启动RocketMQ.使用最新版本的RocketMQ,版本为4.2.0
,安装后启动相关服务.
启动nameServer
:
nohup mqnamesrv &
由于broker
和nameServer
在同一台机器上,启动broker
:
nohup mqbroker -n localhost:9876 &
接下来运行producer
的demo,但是在运行demo的过程中出现了问题,异常堆栈如下:
org.apache.rocketmq.client.exception.MQClientException: Send [3] times, still failed, cost [3004]ms, Topic: MQ-MSG-TOPICS-TEST, BrokersSent: [iZnqntg5t2znd7Z, iZnqntg5t2znd7Z, iZnqntg5t2znd7Z]
See http://rocketmq.apache.org/docs/faq/ for further details.
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:544)
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1069)
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1023)
at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:214)
at core.RocketMQProducer.send(RocketMQProducer.java:44)
at core.RocketMQProducerTest.main(RocketMQProducerTest.java:20)
Caused by: org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to <172.18.46.234:10909> failed
at org.apache.rocketmq.remoting.netty.NettyRemotingClient.invokeSync(NettyRemotingClient.java:388)
at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessageSync(MQClientAPIImpl.java:351)
at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:335)
at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:298)
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendKernelImpl(DefaultMQProducerImpl.java:696)
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:463)
... 5 more
现将问题的排查及解决方法记录如下.
遇到这个问题后,根据异常信息知道是无法连接到172.18.46.234:10909
.那么这个地址是nameServer
的还是broker
的呢?
在demo中配置的nameServer
地址是阿里云服务器的公网地址,而且端口号是默认的9876,因此172.18.46.234:10909
是broker
的地址,即producer
尝试连接broker
时失败.
后来发现172.18.46.234
是我的阿里云服务器的内网地址,我们回忆下RocketMQ的工作流程:
nameServer
broker
启动时使用-n localhost:9876
指定nameServer
,将自己的IP地址注册到nameServer
producer
首先连接nameServer
,获取可用的broker
地址producer
根据从nameServer
获取的broker
地址,将信息发送给broker
因此错误的原因是broker
将自己内网地址发送给了nameServer
,producer
从nameServer
获取的是broker
的内网IP地址,自然无法连接broker
.
由上可知,只要broker
将自己的外网地址注册到nameServer
即可,查阅资料发现,broker
注册到nameServer
的地址可以使用参数配置,参数为brokerIP1
和brokerIP2
(个人推测这两个IP应该一个是普通channel,一个是vip channel),但是如何在broker
启动时设置这两个参数呢?
查看RocketMQ中org.apache.rocketmq.broker.BrokerStartup
源码可知,broker启动选项如下:
短选项 | 长选项 | 是否必填 | 是否需要参数 | 参数是否可选 | 含义 |
---|---|---|---|---|---|
n | nameSrvAddr | 否 | 是 | 否 | Name server address list, eg: 192.168.0.1:9876;192.168.0.2:9876 |
c | configFile | 否 | 是 | 否 | Broker config properties file |
h | help | 否 | 否 | - | Print help |
p | printConfigItem | 否 | 否 | - | Print all config item |
m | printImportantConfig | 否 | 否 | - | Print important config item |
备注:
由上可知,启动broker
时共有5个选项,比较常用的是-n
和-c
,-n
用于指定nameServer
的IP地址及端口号,-c
用于指定配置文件,同时nameServer
的IP地址及端口号也可用nameSrvAddr
在配置文件中设置,这样启动broker
时就不用使用选项指定nameServer
了.
RocketMQ在$ROCKETMQ_HOME/conf
下提供了几个默认的配置文件,如2m-2s-async
文件夹中提供了4个配置文件,分别是2个master
和2个slave
,且master
和slave
使用异步方式同步数据;而broker.conf
提供了集群中只有一个master
,无slave
的配置文件,在配置文件中添加brokerIP1
,brokerIP2
,nameSrvAddr
属性,内容如下:
brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
# brokerIP1和brokerIP2默认获取本地ip地址,在云服务器上会获取内网ip地址,因此必须显式设置
brokerIP1=*.*.*.*
brokerIP2=*.*.*.*
# 将namesrvAddr设置在configfile中
namesrvAddr=localhost:9876
其中brokerIP1
和brokerIP2
修改为自己云服务器的外网地址.
启动broker
(假设当前工作目录为$ROCKETMQ_HOME/bin
):
nohup mqbroker -c ../conf/broker.conf &
再次运行demo,即可成功向broker
发送消息.
broker
向nameServer
注册的地址是brokerIP1
,brokerIP2
,
@ImportantField
private String brokerIP1 = RemotingUtil.getLocalAddress();
private String brokerIP2 = RemotingUtil.getLocalAddress();
在阿里云服务器上获取的是内网IP地址,因此producer
从nameServer
获取的broker
地址是broker
的内网IP地址,无法向broker
发送消息.
可以通过-c
指定broker
启动时的配置文件,在配置文件中添加brokerIP1
和brokerIP2
属性,其值为阿里云服务器的外网IP地址,即可解决上述问题.
除此之外,RocketMQ还提供了-h
,-p
,-m
用于打印帮助,打印配置项和打印重要配置项供用户参考.
还可将nameServer
的地址通过nameSrvAddr
属性写在配置文件中,这样启动时就无需通过-n
设置nameServer
地址.