背景

相信大家看到这个文章对消息服务器已经不陌生了,笔者也是在平日无聊想着自己编写一套关于RockerMQ 的消息灰度框架的时候,准备本地搭建一个RockerMQ服务环境时遇到了一个头疼的问题。在执行RockerMQ官网的Topic创建的时候(sh bin/mqadmin updatetopic -n localhost:9876 -t TestTopic),出现了报错/Library/Internet: No such file or directory

报错分析

在整个启动rockermq的NameServer和Broker的时候都是很顺利的,只是 bin/mqadmin updatetopic 执行命令报错,

报错内容:

[XX:rocketmq-5.0.0 baizhuangli$ sh bin/mqadmin updatetopic -n localhost:9876 -t TestTopic/Users/xiaobai/Documents/mw/rocketmq-all-5.0.0-source-release/distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/tools.sh: line 56: /Library/Internet: No such file or directoryxiaobaizhuangli:rocketmq-5.0.0 baizhuangli$ pwd

我们针对性的对执行文件进行分析:/bin/tools.sh: line 56: /Library/Internet: No such file or directory

打开报错的执行文件,在报错行的前面增加一下输出要执行的内容。

 # 增加输出目标错误行(56行执行的内容) 55 echo $JAVA  ${JAVA_OPT} "$@" 56 $JAVA ${JAVA_OPT} "$@"

再次执行创建topic的语句,可以看到输出的语句中其实是想要执行类

[xiaobai:rocketmq-5.0.0 baizhuangli$ sh bin/mqadmin updatetopic -n localhost:9876 -t TestTopic/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java -server -Xms1g -Xmx1g -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -cp .:/Users/xiaobai/Documents/mw/rocketmq-all-5.0.0-source-release/distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/../conf:/Users/xiaobai/Documents/mw/rocketmq-all-5.0.0-source-release/distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/../lib/*: org.apache.rocketmq.tools.command.MQAdminStartup updatetopic -n localhost:9876 -t TestTopic/Users/xiaobai/Documents/mw/rocketmq-all-5.0.0-source-release/distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/tools.sh: line 56: /Library/Internet: No such file or directory

执行的目标语句如下,本质上我们可以直接拷贝出来进行执行。
/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java -server -Xms1g -Xmx1g -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -cp .:/Users/xiaobai/Documents/mw/rocketmq-all-5.0.0-source-release/distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/../conf:/Users/xiaobai/Documents/mw/rocketmq-all-5.0.0-source-release/distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/../lib/*: org.apache.rocketmq.tools.command.MQAdminStartup updatetopic -n localhost:9876 -t TestTopic

当你拷贝语句直接执行之后,依旧会报错,提示-bash: /Library/Internet: No such file or directory;

好的,到此我们可以看到/Library/Internet 这个命令压根就不是一个可执行的命令。是不是很奇怪,这个这个脚本中$JAVA=$JAVA_HOME怎么是 /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java, 而不是我们自己下载安装的JDK的路径呢?我们本地查看一下JAVA_HOME的取值明明是/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home 才对呀。
是的,我们自己定义环境变量是在~/.bash_profile中,但是这个启动脚本读取的是系统里设置的。当我们java_home的lib存在两个选项时,系统默认使用了第一个。也就是

/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home,所以导致我们命令执行失败了。

通过 /usr/libexec/java_home -V 查看可选择的lib和系统使用的选项。(这个地方地方目前没有比较好的切换为自己版本的方式)

[xiaobai:Internet Plug-Ins baizhuangli$ /usr/libexec/java_home -V Matching Java Virtual Machines (2):    1.8.121.13 (x86_64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home    1.8.0_121 (x86_64) "Oracle Corporation" - "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home

扩展一下 /Library/Internet Plug-Ins是Mac系统下 插件集合

[xiaobaizhuangli:Internet Plug-Ins baizhuangli$ cd /Library/Internet\ Plug-Ins[xiaobaizhuangli:Internet Plug-Ins baizhuangli$ pwd/Library/Internet Plug-Ins[xiaobaizhuangli:Internet Plug-Ins baizhuangli$ lsDM_CCB_NetSignPlugin.pluginHDZB_CCB_SignPlugin.pluginTDR_CCB_NetSignPlugin.pluginflashplayer.xptFlash Player.pluginJavaAppletPlugin.pluginWD_CCB_NetSignPlugin.plugin# 其中JavaAppletPlugin.plugin 下面都是JAVA的JDK

问题修复

上面我们既然已经找到了问题,那么我们只需要将脚本【rocketmq-5.0.0/bin/tools.sh】中获取java_home的地方修改为我们自己下载安装的JDK的lib目录理论上就可以。这样就不会因为/Library/Internet Plug-Ins/中间存在空格,导致可执行命令找到不到的问题了。我们对执行的执行的脚本做一些简单修改,主要是export自己的jdk路径到环境变量中。

# 上面省略了一下内容,find_java_home# 在JAVA_HOME获取之前,通过export命令将自己的JDK的HOME设置到环境变量,从而对本次启动生效export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java[ ! -e "$JAVA_HOME/bin/java" ] && error_exit "Please set the JAVA_HOME variable in your environment, We need java(x64)!"export JAVA_HOMEexport JAVA="$JAVA_HOME/bin/java"export BASE_DIR=$(dirname $0)/..export CLASSPATH=.:${BASE_DIR}/conf:${BASE_DIR}/lib/*:${CLASSPATH}#===========================================================================================# JVM Configuration#===========================================================================================JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m"JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"$JAVA ${JAVA_OPT} "$@"

然后再次执行我们的语句,可以看到已经我们的修改已经生效。

[xiaobai:rocketmq-5.0.0 baizhuangli$ sh bin/mqadmin updatetopic -n localhost:9876 -t TestTopic/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/bin/java -server -Xms1g -Xmx1g -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -cp .:/Users/xiaobai/Documents/mw/rocketmq-all-5.0.0-source-release/distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/../conf:/Users/xiaobai/Documents/mw/rocketmq-all-5.0.0-source-release/distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/../lib/*: org.apache.rocketmq.tools.command.MQAdminStartup updatetopic -n localhost:9876 -t TestTopic

总结

对于我们JAVA开发同学来说,经常会自己下载指定版本的JDK包来运行我们的程序。通常编写代码和运行的时候,我们可以在IEDA上选择我们JDK版本及lib的路径,或者使用的不是/Library/Internet Plug-Ins/ 这种带有空格包下的目录,几乎不会出现这种问题。

但是使用源码启动一些工具或者开源项目时,开源项目的shell脚本中不会完全考虑到我们每个开发者的本地环境,就会莫名其妙的报一些错误。这次是对于Rocket MQ的调试出现的这个问题,当然对于其他的项目(例如 Nacos )也发现过这种问题。

所以我们要在出现错误时,仔细的分析报错内容和报错来源。进入报错产生的地方,通过输出打印、设置预期值等方式来进行调试和错误排查。