本文介绍的主要内容是:启动broker的一些错误日志的处理,以及由于namesrvAddr为null,broker不能正确注册到name server的排查过程。结论是应该配置name server的地址值(环境变量)。
问题背景:
- 从github下载RocketMQ源码
- 根据对RocketMQ原理的理解,在idea中配置好本地运行环境,先启动name server模块,再启动broker模块,因为broker要将一些信息注册到name server上(name server角色相当于微服务中的eureka消息注册中心)。
- 我遇到的一些问题:
(1) 我是windows环境,在windows下,环境变量user.home对应的是Desktop的上一层路径,所以我的环境,user.home是“C:\Users\Administrator”。
(2) 找到日志文件打印在哪里,下面是broker的配置文件
所以在windows上日志文件路径是:
(3) 先启动nameserver,启动类是:src/main/java/org/apache/rocketmq/namesrv/NamesrvStartup.java
,再启动broker,启动类是:src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
。这里正常操作应该查看两个模块的日志,因为控制台并不打印日志,我的一个错误:在控制台发现没报错,就去启动了example下的producer和consumer,准备测试消息的生产和消费。但producer启动报错了。
于是才想着去看日志…在broker.log发现了error
思考为什么会找不到这个路径下的文件,还是因为user.home在windows下的路径是Desktop的上一层,这里解决办法可以是修改源码,将这些文件的位置自定义,因为源码是:
或者直接在该默认路径下手动建立不存在的文件。所以broker启动日志不再报文件不存在这个类型的error。虽然nameserver和broker启动都不报错了,但是producer仍报上述错误。
(4) 再启动producer仍报上述错误,直接Debug broker的启动流程,下面是排除出问题的部分相关源码与解释:
public class BrokerStartup {
public static void main(String[] args) {
//需要createBrokerController返回一个brokerController,再调用start()启动
start(createBrokerController(args));
}
public static BrokerController start(BrokerController controller) {
try {
controller.start();
String tip = "The broker[" + controller.getBrokerConfig().getBrokerName() + ", "
+ controller.getBrokerAddr() + "] boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();
//在做判断的地方打断点,如果正常,broker应该注册到nameserver,
//所以broker.log里应该打印出日志:应该包含and name server is,
//但是日志里并没有打印出这句日志,就说明这个判断条件失败了!!!
//controller.getBrokerConfig().getNamesrvAddr()是null。见后面调试截图。
if (null != controller.getBrokerConfig().getNamesrvAddr()) {
tip += " and name server is " + controller.getBrokerConfig().getNamesrvAddr();
}
log.info(tip);
System.out.printf("%s%n", tip);
return controller;
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}
public static BrokerController createBrokerController(String[] args) {
//省略部分源码...
//这里打断点,因为namesrvAddr是从brokerConfig中get的
final BrokerConfig brokerConfig = new BrokerConfig();
final NettyServerConfig nettyServerConfig = new NettyServerConfig();
final NettyClientConfig nettyClientConfig = new NettyClientConfig();
//省略部分源码...
if (BrokerRole.SLAVE == messageStoreConfig.getBrokerRole()) {
int ratio = messageStoreConfig.getAccessMessageInMemoryMaxRatio() - 10;
messageStoreConfig.setAccessMessageInMemoryMaxRatio(ratio);
}
//省略部分源码...
//这里拿到namesrvAddr,发现是null
String namesrvAddr = brokerConfig.getNamesrvAddr();
if (null != namesrvAddr) {
try {
String[] addrArray = namesrvAddr.split(";");
for (String addr : addrArray) {
RemotingUtil.string2SocketAddress(addr);
}
} catch (Exception e) {
System.out.printf(
"The Name Server Address[%s] illegal, please set it as follows, \"127.0.0.1:9876;192.168.0.1:9876\"%n",
namesrvAddr);
System.exit(-3);
}
}
//省略部分源码...
}
debug发现new的brokerConfig实例中namesrvAddr=null,所以导致在start()方法中,if条件拿到的namesrvAddr是null,所以导致日志没有打印出带有“and name server is”信息的日志。
if (null != controller.getBrokerConfig().getNamesrvAddr()) {
tip += " and name server is " + controller.getBrokerConfig().getNamesrvAddr();
}
进一步查看BrokerConfig类,调用默认的构造,其中的属性有很多的默认值:
说明属性namesrvAddr就是在这里没有赋值成功,才返回null。正常情况下应该通过系统jvm参数获得namesrvAddr的值,如果获取不到,则通过环境变量NAMESRV_ADDR获得。而这里没有成功获取是因为没有配置name server地址。
解决办法如下:
- 由于在本机运行,所以namesrvAddr的值应该是127.0.0.1:9876,而这里直接
private String namesrvAddr = "127.0.0.1:9876";
【不推荐,在代码里写死了,应 配置>编码】 - 设置环境变量
NAMESRV_ADDR
的值,就像当初需要设置ROCKET_HOME
环境变量一样。 - 我采用的是配置
ROCKET_HOME
环境变量的方式。可以成功获得namesrvAddr的值。 - JVM参数:- rocketmq.namesrv.addr=127.0.0.1:9876
- 编码层面:setNamesrvAddr
再启动broker,可以打印日志:
表示broker成功拿到nameserver的地址并成功注册到nameserver。
再开启producer不再报错,可以正常生产和消费消息啦~
其实,在github源码的wiki里有这样一段话:
加油!