最详细JMX远程连接服务器Zookeeper失败问题踩坑和总结
环境:Ubuntu18.04服务器 Zookeeper3.4.6版本 伪分布式
这里不从零介绍JMX配置的基本教程了,那些随便搜一下都有,主要介绍一下博主在使用服务器配置JMX时遇到的坑。
想要远程查看和监控Zookeeper运行情况,JMX是一种不错的选择。显示如下:
若想通过 jmx监控Zookeeper运行情况,需在启动参数(zkServer.sh)增加jmx配置。
网上大体都是如下:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.local.only=false
-Djava.rmi.server.hostname=192.168.180.1
-Dcom.sun.management.jmxremote.port=8099
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
或者是:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY
-Djava.rmi.server.hostname=$JMXHOSTNAME
-Dcom.sun.management.jmxremote.port=$JMXPORT
-Dcom.sun.management.jmxremote.authenticate=$JMXAUTH
-Dcom.sun.management.jmxremote.ssl=$JMXSSL
-Dcom.sun.management.jmxremote.access.file=$JMXACCESS
-Dcom.sun.management.jmxremote.password.file=$JMXPASSWORD
-Dzookeeper.jmx.log4j.disable=$JMXLOG4J
org.apache.zookeeper.server.quorum.QuorumPeerMain
#然后在conf下创建 java.env 文件给以上变量赋值。
对于以上配置,本地环境(本地虚拟机)可以,如果是远程(服务器)的话,这样是不行的。
在Java启动时,JMX会绑定一个接口,RMI也会绑定一个接口,在复杂网络环境下,有可能你通过打开防火墙允许了JMX端口的通过,但是由于没有放行RMI,远程连接也是会失败的。
这是因为 JMX在远程连接时,会随机开启一个RMI端口作为连接的数据端口,这个端口会被防火墙给阻止,以至于连接超时失败。 在Java7u25版本后,可以使用
-Dcom.sun.management.jmxremote.rmi.port
参数来指定这个端口;好消息是,你可以将这个端口和jmx.port的端口设置成一个端口,这样防火墙就只需要放行一个端口就可以了。
增加 rmi 端口配置:
-Dcom.sun.management.jmxremote.rmi.port=8099
加了后,可以远程连接Zookeeper了。
注意: 需要检查 hostname -i,看解析出来是否包含本地的IP,如果输出不包含公网IP,会连接不上。
配置正确的显示如下:
[user ~]$ hostname -i
内网IP 公网IP
[user ~]$
另外:
zkServer.sh文件中有两个ZOOMAIN,很多小伙伴不知道应该改哪一个,这里简略分析一下:
#以下是zkServer.sh文件中的部分内容
if [ "x$JMXDISABLE" = "x" ] || [ "$JMXDISABLE" = 'false' ]
then
echo "ZooKeeper JMX enabled by default" >&2
if [ "x$JMXPORT" = "x" ] #这里说如果未配置JMXPORT变量,则执行第一个ZOOMAIN中的配置
then
ZOOMAIN="-Dcom.sun.management.jmxremote
-org.apache.zookeeper.server.quorum.QuorumPeerMain"
else #如果JMXPORT变量不为空,则执行第二个ZOOMAIN中的配置
if [ "x$JMXAUTH" = "x" ]
then
JMXAUTH=false
fi
if [ "x$JMXSSL" = "x" ]
then
JMXSSL=false
fi
if [ "x$JMXLOG4J" = "x" ]
then
JMXLOG4J=true
fi
echo "ZooKeeper remote JMX Port set to $JMXPORT" >&2
echo "ZooKeeper remote JMX authenticate set to $JMXAUTH" >&2
echo "ZooKeeper remote JMX ssl set to $JMXSSL" >&2
echo "ZooKeeper remote JMX log4j set to $JMXLOG4J" >&2
ZOOMAIN="-Dcom.sun.management.jmxremote
org.apache.zookeeper.server.quorum.QuorumPeerMain"
fi
else
echo "JMX disabled by user request" >&2
ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain"
fi
执行不同ZOOMAIN最直观的区别就是:
# 执行第一个ZOOMAIN
[user ~]$ zkServer.sh start zoo.cfg
ZooKeeper JMX enabled by default
Using config: /usr/zookeeper-3.4.14/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
# 执行第二个ZOOMAIN
[user ~]$ zkServer.sh start zoo.cfg
ZooKeeper JMX enabled by default
ZooKeeper remote JMX Port set to "你的ip或主机名"
ZooKeeper remote JMX Port set to 8099
ZooKeeper remote JMX authenticate set to true
ZooKeeper remote JMX ssl set to false
ZooKeeper remote JMX log4j set to true
Using config: /usr/zookeeper-3.4.14/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
所以,如果直接把参数值写到属性后面,就需要将启动参数写到第一个ZOOMAIN里面;如果你想使用java.env来统一管理的话,就需要将启动参数写到第二个ZOOMAIN里面;还有一种 懒人方案:两个ZOOMAIN都写上启动参数!
因为我的是伪分布式zookeeper,需要配置三个不同的JMXPORT,所以我选择了第一种方案,直接将参数写到后面:
# 将配置好的zkServer.sh复制两份,并分别修改每个文件中的
# -Dcom.sun.management.jmxremote.port
# 和
# -Dcom.sun.management.jmxremote.rmi.port
# 以保证不会发生端口冲突
[user ~]$ cp zkServer.sh zkServer2.sh
[user ~]$ cp zkServer.sh zkServer3.sh
# 启动时三个zkServer.sh使用三个zoo.cfg
总结:
JMX 参数完整配置:
ZOOMAIM="
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.local.only=false
-Djava.rmi.server.hostname=你的公网ip
-Dcom.sun.management.jmxremote.port=8099 # 在复制的文件中修改端口
-Dcom.sun.management.jmxremote.rmi.port=8099 # 在复制的文件中修改端口
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.access.file=用户权限文件路径
-Dcom.sun.management.jmxremote.password.file=密码文件路径
-Dzookeeper.jmx.log4j.disable=false
org.apache.zookeeper.server.quorum.QuorumPeerMain
"