jvisualvm+jmx监听docker容器中的jvm情况

1、springboot项目,启用jmx

在启动参数中增加如下配置:

-Djava.rmi.server.hostname=ip
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=1199
-Dcom.sun.management.jmxremote.authenticate=false //关闭权限校验
-Dcom.sun.management.jmxremote.ssl=false//关闭ssl校验

这里我们没有启用权限验证,如何进行权限验证,请自行搜索。

2、通过jvisualvm连接远程服务器

如图:在远程中,右键添加远程主机,输入主机ip,确定后,在主机ip上右键选择jmx连接,输入配置的端口即可。
jvisualvm+jmx监听docker容器中的jvm情况_第1张图片

3、正常情况下,这时候我们就可以看到jvm信息了。

如果你是部署在docker一样的容器中,那么恭喜你,你将看到如下报错:
jvisualvm+jmx监听docker容器中的jvm情况_第2张图片
你没看错,这就是网上常说的 无法使用service:jmx:rmi:///jndi/rmi://ip:port/jmxrmi连接到ip:port错误。

4、异常分析

在你尝试了网上说的,hostname-i确认修改,等手段后,你会发现最终指向的是jmx在主进程外,启动的默认服务端口是随机的。而我们的docker容器无法动态映射这个端口。还有说这是jdk5的失误。
事实上,确实是由于这个服务端口是随机生成的,导致我们请求注册服务后,返回的服务调用不通导致,但是是不是真的无法配置呢?看源码:

4.1在JMXConnectorServer的如下代码初加断点

public JMXConnectorServer(MBeanServer mbeanServer) {
    this.mbeanServer = mbeanServer;
}

4.2一层层调用栈逆向查找,找到ConnectorBootstrap类

public static synchronized JMXConnectorServer startRemoteConnectorServer(String var0, Properties var1) {
    int var2;
    try {
        var2 = Integer.parseInt(var0);
    } catch (NumberFormatException var29) {
        throw new AgentConfigurationError("agent.err.invalid.jmxremote.port", var29, new String[]{var0});
    }

    if (var2 < 0) {
        throw new AgentConfigurationError("agent.err.invalid.jmxremote.port", new String[]{var0});
    } else {
//这里的var3就是rmiurl中服务端要设置的地址。我们可以看到如果没有设置的话按照0处理,也就是随机值,这个值也可以通过-Dcom.sun.management.jmxremote.rmi.port指定。至此我们就解决了随机端口的问题
        int var3 = 0;
        String var4 = var1.getProperty("com.sun.management.jmxremote.rmi.port");
        try {
            if (var4 != null) {
                var3 = Integer.parseInt(var4);
            }
        } catch (NumberFormatException var30) {
            throw new AgentConfigurationError("agent.err.invalid.jmxremote.rmi.port", var30, new String[]{var4});
        }

        if (var3 < 0) {
            throw new AgentConfigurationError("agent.err.invalid.jmxremote.rmi.port", new String[]{var4});
        } else {
            String var5 = var1.getProperty("com.sun.management.jmxremote.authenticate", "true");
            boolean var6 = Boolean.valueOf(var5);
            String var7 = var1.getProperty("com.sun.management.jmxremote.ssl", "true");
            boolean var8 = Boolean.valueOf(var7);
            String var9 = var1.getProperty("com.sun.management.jmxremote.registry.ssl", "false");
            boolean var10 = Boolean.valueOf(var9);
            String var11 = var1.getProperty("com.sun.management.jmxremote.ssl.enabled.cipher.suites");
            String[] var12 = null;
            if (var11 != null) {
                StringTokenizer var13 = new StringTokenizer(var11, ",");
                int var14 = var13.countTokens();
                var12 = new String[var14];

                for(int var15 = 0; var15 < var14; ++var15) {
                    var12[var15] = var13.nextToken();
                }
            }

            String var31 = var1.getProperty("com.sun.management.jmxremote.ssl.enabled.protocols");
            String[] var32 = null;
            if (var31 != null) {
                StringTokenizer var33 = new StringTokenizer(var31, ",");
                int var16 = var33.countTokens();
                var32 = new String[var16];

                for(int var17 = 0; var17 < var16; ++var17) {
                    var32[var17] = var33.nextToken();
                }
            }

            String var34 = var1.getProperty("com.sun.management.jmxremote.ssl.need.client.auth", "false");
            boolean var35 = Boolean.valueOf(var34);
            String var36 = var1.getProperty("com.sun.management.jmxremote.ssl.config.file");
            String var18 = null;
            String var19 = null;
            String var20 = null;
            if (var6) {
                var18 = var1.getProperty("com.sun.management.jmxremote.login.config");
                if (var18 == null) {
                    var19 = var1.getProperty("com.sun.management.jmxremote.password.file", getDefaultFileName("jmxremote.password"));
                    checkPasswordFile(var19);
                }

                var20 = var1.getProperty("com.sun.management.jmxremote.access.file", getDefaultFileName("jmxremote.access"));
                checkAccessFile(var20);
            }

            String var21 = var1.getProperty("com.sun.management.jmxremote.host");
            String var22 = var1.getProperty("com.sun.management.jmxremote.serial.filter.pattern");
            if (log.debugOn()) {
                log.debug("startRemoteConnectorServer", Agent.getText("jmxremote.ConnectorBootstrap.starting") + "\n\t" + "com.sun.management.jmxremote.port" + "=" + var2 + (var21 == null ? "" : "\n\tcom.sun.management.jmxremote.host=" + var21) + "\n\t" + "com.sun.management.jmxremote.rmi.port" + "=" + var3 + "\n\t" + "com.sun.management.jmxremote.ssl" + "=" + var8 + "\n\t" + "com.sun.management.jmxremote.registry.ssl" + "=" + var10 + "\n\t" + "com.sun.management.jmxremote.ssl.config.file" + "=" + var36 + "\n\t" + "com.sun.management.jmxremote.ssl.enabled.cipher.suites" + "=" + var11 + "\n\t" + "com.sun.management.jmxremote.ssl.enabled.protocols" + "=" + var31 + "\n\t" + "com.sun.management.jmxremote.ssl.need.client.auth" + "=" + var35 + "\n\t" + "com.sun.management.jmxremote.authenticate" + "=" + var6 + (var6 ? (var18 == null ? "\n\tcom.sun.management.jmxremote.password.file=" + var19 : "\n\tcom.sun.management.jmxremote.login.config=" + var18) : "\n\t" + Agent.getText("jmxremote.ConnectorBootstrap.noAuthentication")) + (var6 ? "\n\tcom.sun.management.jmxremote.access.file=" + var20 : "") + "");
            }

            MBeanServer var23 = ManagementFactory.getPlatformMBeanServer();
            JMXConnectorServer var24 = null;
            JMXServiceURL var25 = null;

            try {
                ConnectorBootstrap.JMXConnectorServerData var26 = exportMBeanServer(var23, var2, var3, var8, var10, var36, var12, var32, var35, var6, var18, var19, var20, var21, var22);
                var24 = var26.jmxConnectorServer;
                var25 = var26.jmxRemoteURL;
                log.config("startRemoteConnectorServer", Agent.getText("jmxremote.ConnectorBootstrap.ready", var25.toString()));
            } catch (Exception var28) {
                throw new AgentConfigurationError("agent.err.exception", var28, new String[]{var28.toString()});
            }

            try {
                HashMap var37 = new HashMap();
                var37.put("remoteAddress", var25.toString());
                var37.put("authenticate", var5);
                var37.put("ssl", var7);
                var37.put("sslRegistry", var9);
                var37.put("sslNeedClientAuth", var34);
                ConnectorAddressLink.exportRemote(var37);
            } catch (Exception var27) {
                log.debug("startRemoteConnectorServer", var27);
            }

            return var24;
        }
    }
}

重点看看这段代码:

  int var3 = 0;
        String var4 = var1.getProperty("com.sun.management.jmxremote.rmi.port");
        try {
            if (var4 != null) {
                var3 = Integer.parseInt(var4);
            }
        } catch (NumberFormatException var30) {
            throw new AgentConfigurationError("agent.err.invalid.jmxremote.rmi.port", var30, new String[]{var4});
        }

对,你没看错默认是0,是随机端口,但是我们是可以通过-Dcom.sun.management.jmxremote.rmi.port参数指定的。

5、最终配置

-Djava.rmi.server.hostname=ip
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.rmi.port=11199
-Dcom.sun.management.jmxremote.port=1199
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

同时将1199和11199端口映射到宿主机上即可。
到这里容器下的jmx监控就完成了。

jmx的参考资料
csdn的一个文章介绍

你可能感兴趣的:(jvisualvm+jmx监听docker容器中的jvm情况)