一、前言(简称废话)
VisualVM是到目前为止随JDK发布的功能最强大的运行监视和故障处理程序,它除了运行监视、故障处理外,还提供了很多其他方面的性能。如性能分析(Profiling),VisualVM的性能分析功能甚至比起JProfiler等专业且收费的Profiling工具都不会逊色多少,而且VisualVM还有一个很大的优点:不需要被监视的程序基于特殊Agent运行,因此它对应用程序的实际性能的影响很小,使得它可以直接应用在生产环境中。这个优点是JProfiler等工具无法与之媲美的。
本人之前也用过JProfiler,但是从来没在生产环境使用过,一是因为它使用了Agent的技术进行性能监控,对程序影响较大,二是它的操作性觉得不方便。后面选择了VisualVM这款小而巧的官方工具。
使用VisualVM监控远程主机,主要是要在远程主机上部署JMX服务和jstat服务,jstat服务的部署花费了我大概一天的时间,主要是阿里云主机的网络分本地IP和公网IP,所以网络是个问题。而且,网上的资料良莠不齐,没有一篇文章能真正地讲解如何进行监控。
二、效果预览
三、环境
1. 客户端环境
- JDK1.8
- mac系统
- jvisual
2. 服务器环境
- JDK1.8
- 阿里云主机,CentOS release 6.8 (Final)
- 公网IP:66.22.33.44 私有IP:192.168.1.50 (这两个网络很重要)
3. 确定服务器的主机名和IP是对应的
hostname -i
# 如果匹配的结果是无法识别或者127.0.0.1, 则需要手动配置主机名和IP映射,假设主机名是aliyunlinux
vim /etc/hosts
# 添加一行。记住这里填的是私有IP,不能是公有IP,不能是公有IP,否则你会被坑得连自己姓什么都不知道
192.168.1.50 aliyunlinux
四、部署服务
1. 部署JMX服务
1.1 修改catalina.sh文件
# 进入tomcat的bin目录,修改catalina.sh:
vim catalina.sh
# 添加如下配置:
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access -Djava.rmi.server.hostname=66.22.33.44"
# -Dcom.sun.management.jmxremote.ssl 是否开启ssl传输,设为false即可
# -Dcom.sun.management.jmxremote.authenticate 是否校验,设为false即可
# -Dcom.sun.management.jmxremote.password.file 远程登录的账号管理文件,下文会讲到如何获取
# -Dcom.sun.management.jmxremote.access.file 同上
# -Djava.rmi.server.hostname 这个很重要,如果不填的话,可以重启成功,按时客户端的jvisual是无法远程连接上的,这里的IP必须是公网IP,公网IP
==补充:有些文章会说在tomcat的bin目录新建一个setenv.sh文件,然后添加以下内容。亲测无效,在重启tomcat的时候会报错==
#!/bin/sh
export JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password -Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access"
# 报错报文
Caused by: java.lang.IllegalArgumentException: jmxremote.access (没有那个文件或目录)
at javax.management.remote.rmi.RMIConnectorServer.start(RMIConnectorServer.java:375)
at org.apache.catalina.mbeans.JmxRemoteLifecycleListener.createServer(JmxRemoteLifecycleListener.java:334)
at org.apache.catalina.mbeans.JmxRemoteLifecycleListener.lifecycleEvent(JmxRemoteLifecycleListener.java:288)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:95)
at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
at org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:388)
at org.apache.catalina.util.LifecycleBase.setState(LifecycleBase.java:333)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:780)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
... 7 more
配置效果截图
1.2 添加jmx的端口监听
# 进入tomcat的conf:
vim server.xml
# 添加如下的监听配置。其中10001和10002是自定义的两个端口,用于远程连接需要
<!-- 远程监控 -->
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener" rmiRegistryPortPlatform="10001" rmiServerPortPlatform="10002" />
配置效果截图
1.3 添加jmx的访问控制文件
# 进入tomcat/conf的目录
# 执行以下命令
cp $JAVA_HOME/jre/lib/management/jmxremote.password.template jmxremote.password
cp $JAVA_HOME/jre/lib/management/jmxremote.access jmxremote.access
vi jmxremote.password
# 取消下面两行的注释,取消注释表示你可以使用这两个账号进行远程登录
#monitorRole QED
#controlRole R&D
# 也可以自定义账号,格式如 username passwd,如我想要添加一个zexin的账号,密码是123
vim jmxremote.password
## 在最后一行添加
zexin 123
## 修改完jmxremote.password文件,还要修改jmxremote.access文件,添加权限
zexin readwrite
1.4 添加catalina-jmx-remote.jar
这里要注意的是,该jar文件现在最高已经到了9.0版本,我的jdk版本是1.8,tomcat版本是7,我下载了9.0的版本重启服务器会报类找不到的异常,所以低于或等于我的配置的同学请选择7.0的版本。下载后copy到tomcat/lib目录。
jar包所在地址:http://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina-jmx-remote
1.5 配置防火墙
其实本人是建议在配置的时候可以把防火墙给关掉,然后在配置成功后再开启防火墙,否则很容易在防火墙这里采坑,而且还不知道是因为防火墙的原因。
vi /etc/sysconfig/iptables
-A INPUT -p tcp -m state --state NEW -m tcp --dport 10001 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 10002 -j ACCEPT
# 重启防火墙
service iptables restart
1.6 重启服务器,本地开启远程监控(测试)
本地开启jvisual,添加远程连接
添加完远程主机之后,在相应的远程主机标签上右键选择添加jmx连接,输入相应的端口号和账号密码.IP即在1.2步骤配置的rmiRegistryPortPlatform,账号密码是在1.3步骤中配置的,不要SSL连接
1.7 成功
2. 部署jstat服务
在部署完jmx服务的时候,你会发现visual gc这个插件无法使用,如果要使用这个插件的话,还需要安装jstat服务,才能监控内存状况,当然jconsole,JProfiler都是可以监控内存状况,这个插件我觉得还是挺直观的。
在开始部署之前,建议先把防火墙给关掉,因为下面的部署涉及到一个动态端口的生成,如果每次都要在启动jstat服务后,再去查看端口占用,再去修改防火墙很麻烦,而且有时候会因为防火墙的问题导致一些乱七八糟的问题出现,所以在第一次部署的时候闲关掉防火墙,等部署成功,再根据相应的端口后开启防火墙。
2.1 配置安全策略
cd $JAVA_HOME/bin
# 新建安全策略文件
vim jstatd.all.policy
## 添加如下内容:
grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
## 有些文章会在$JAVA_HOME/jre/lib/security/java.policy直接修改,本人我是不推荐的,能不动
到原来的配置文件进来就不要碰到,而且在java.policy直接修改,赋予的权限太高,也不安全。
2.2 启动jstat
cd $JAVA_HOME/bin
# 启动命令:(启动成功后,没有报错且有日志输出就是正常的)
./jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=66.22.33.44 -J-Djava.rmi.server.logCalls=true
## 解释:
# -J-Djava.rmi.server.hostname=66.22.33.44 这里要填的是公网IP
# -J-Djava.rmi.server.logCalls=true 是否开启日志,第一次配置最好开启,可以确定是否启动成功,如果是已经配置很多次了,能确保成功的,建议就把该日志关掉,因为该日志的产生量不小,容易撑爆硬盘
## 在生产环境的配置如下(去掉日志打印,加入后台运行):
./jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=66.22.33.44 &
启动失败,排查步骤
- 防火墙是否开启,关掉(正常跑动之后再开启相应端口)
- hostname是否配置正确
- 安全策略是否正确配置,是否有语法错误
- -J-Djava.rmi.server.hostname是否有配置正确的IP地址
配置防火墙
如果不想关闭防火墙,那么就需要针对相应的端口进行放行了,上面的启动命令默认开启的端口是1099,为了不让大家混淆,所以不建议大家去自定义启动端口,使用默认即可,自定义端口也很简单,上面的启动命令加入 -p 1111即可。但是在启动jstat的时候,系统会随机分配一个端口,该端口也要在防火墙配置。可以看到除了1099外,还有个42067的端口,所以该端口也要加入防火墙中(这个随机端口重启后会变化)
netstat -anp | grep jstatd
2.3 测试
第一次配置的话,一般需要把jvisual程序关掉再启动,这个时候jvisual会自动连接jstat服务,端口默认1099,如果你改了端口,要在jvisual中进行添加。(添加这个的比较简单,就是个右键的事,就不多说了)
五、总结
远程监控的整个流程到这也就结束了,jvisual是个好东西,我打算把它应用到生产环境中进行性能监控,大家如果有什么疑惑的地方,可以加我的qq:756795605,一起探讨