【问题标题】:How to access Spring-boot JMX remotely如何远程访问 Spring-boot JMX
【发布时间】:2015-06-07 08:32:55
【问题描述】:

我知道 spring 会自动公开 JMX bean。我可以使用 VisualVM 在本地访问它。

但是,在 prod 上,我如何使用它的 JMX bean 远程连接到应用程序?是否有默认端口或者我应该另外定义什么?

谢谢, 射线。

【问题讨论】:

    标签: spring spring-boot jmx


    【解决方案1】:

    默认情况下,JMX 可以在本地自动访问,因此在本地运行 jconsole 将检测到所有本地 Java 应用程序,而不会暴露端口。

    要通过 JMX远程访问应用程序,您必须指定 RMI 注册表端口。要知道的是,在连接时,JMX 在该端口上初始化,然后随机高 端口上建立数据连接,即如果中间有防火墙,那就是个大问题。 (“嘿,系统管理员,打开一切,好吗?”)。

    要强制 JMX 在您建立的同一端口上重新连接,您有以下几个选项。注意:JMX 和 RMI 可以使用不同的端口,也可以使用相同的端口。

    选项 1:命令行

    -Dcom.sun.management.jmxremote.port=$JMX_REGISTRY_PORT 
    -Dcom.sun.management.jmxremote.rmi.port=$RMI_SERVER_PORT
    

    如果您使用的是 Spring Boot,则可以将其放入您的 (appname).conf 文件中,该文件与您的 (appname).jar 部署一起存在。

    选项 2:Tomcat/Tomee 配置

    配置JmxRemoteLifecycleListener:

    Maven 罐子:

        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-catalina-jmx-remote</artifactId>
            <version>8.5.9</version>
            <type>jar</type>
        </dependency>
    

    配置你的 server.xml:

    <Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
          rmiRegistryPortPlatform="10001" rmiServerPortPlatform="10002" />
    

    选项 3:以编程方式配置

    @Configuration
    public class ConfigureRMI {
    
        @Value("${jmx.rmi.host:localhost}")
        private String rmiHost;
    
        @Value("${jmx.rmi.port:1099}")
        private Integer rmiPort;
    
        @Bean
        public RmiRegistryFactoryBean rmiRegistry() {
            final RmiRegistryFactoryBean rmiRegistryFactoryBean = new RmiRegistryFactoryBean();
            rmiRegistryFactoryBean.setPort(rmiPort);
            rmiRegistryFactoryBean.setAlwaysCreate(true);
            return rmiRegistryFactoryBean;
        }
    
        @Bean
        @DependsOn("rmiRegistry")
        public ConnectorServerFactoryBean connectorServerFactoryBean() throws Exception {
            final ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();
            connectorServerFactoryBean.setObjectName("connector:name=rmi");
            connectorServerFactoryBean.setServiceUrl(String.format("service:jmx:rmi://%s:%s/jndi/rmi://%s:%s/jmxrmi", rmiHost, rmiPort, rmiHost, rmiPort));
            return connectorServerFactoryBean;
        }
    }
    

    您将看到,诀窍在于serviceUrl,您可以在其中同时指定jmx:rmi 主机/端口和jndi:rmi 主机/端口。如果你同时指定,你不会得到随机的高“问题”。

    编辑:要让 JMX 远程处理工作,您需要做出关于身份验证的决定。最好分 3 个不同的步骤来完成:1) 使用 -Dcom.sun.management.jmxremote.authenticate=false 进行基本设置,然后 2) 添加密码文件 (-Dcom.sun.management.jmxremote.password.file)。 See here for instructions. + -Dcom.sun.management.jmxremote.ssl=false 然后 3) 设置 SSL。

    【讨论】:

    • 此方法是否排除使用下面的JAVA_OPTS 方法?
    • 我在 Docker 容器中运行 Java 服务时尝试了这种方法(选项 3),但无法从容器外部连接到它。我将不得不做一个小样本项目来更好地探索这个想法。
    • 这个(appname).conf 文件是否记录在某处?
    • @inanutshellus 完成。 OMG:你可以编辑别人的答案!这是我第一次在 SO 上这样做。我自己的答案:当然。别人的:让我大吃一惊。 :)
    【解决方案2】:

    在“$JAVA_OPTS”(在您的应用程序中)中添加以下 JVM 属性:

    -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=<PORT_NUMBER> -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=<HOST'S_IP>
    

    在 Jconsole/Visual VM 中使用以下连接:

    service:jmx:rmi:///jndi/rmi://<HOST'S_IP>:<PORT_NUMBER>/jmxrmi
    

    它不会启用安全性,但会帮助您连接到远程服务器。

    【讨论】:

    • 这对我有用...谢谢。直到我添加了 -Djava.rmi.server.hostname,它才起作用。我能够像这样通过 jconsole 进行连接:ip:port
    • 这些参数必须通过java 命令行使用。对于 Spring Boot 2.3.2,在 JAVA_OPTS 中设置它们对我不起作用。这是一篇较长的博文:medium.com/@cl4r1ty/…。 (Docker 没关系,JAVA_OPTS="..." java -jar 也不行。)
    【解决方案3】:

    在 Java 1.8.0_71 和 Spring Boot(1.3.3.RELEASE) 上经过测试的方法。 将以下参数附加到受监控 JVM 的 JVM 参数中。

    -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=12348 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.rmi.port=12349 -Dcom.sun.management.jmxremote.password.file=/somewhere/jmxremote.password -Dcom.sun.management.jmxremote.access.file=/somewhere/jmx/jmxremote.access
    

    com.sun.management.jmxremote.port 用于定义固定的 RMI 注册端口,com.sun.management.jmxremote.rmi.port 用于指示 JVM 使用固定的 RMI 端口,但不使用随机端口。

    通过设置,我可以通过防火墙将 JVM 客户端从远程主机连接到受监控的 JVM,只需打开 12348 和 12349 端口。

    我在远程机器上使用java -jar cmdline-jmxclient-0.10.3.jar user:pwd hostip:12348 进行了测试,它生成以下输出(仅为演示而缩短)。

    java.lang:type=Runtime
    java.lang:name=PS Scavenge,type=GarbageCollector
    Tomcat:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/,j2eeType=Filter,name=requestContextFilter
    java.nio:name=mapped,type=BufferPool
    Tomcat:host=localhost,type=Host
    java.lang:name=Compressed Class Space,type=MemoryPool
    .......
    

    jar是从Here下载的。

    【讨论】:

    • 我在 OpenJDK 11 中成功测试了这种方法,有 2 个改进: 1. 为 JMX 和 RMI 使用相同的端口。 2. 省略-Dcom.sun.management.jmxremote:不再需要。
    【解决方案4】:

    另一种选择

    Reference for jmxremote.password and jmxremote.access files

    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.jmx.support.ConnectorServerFactoryBean;
    import org.springframework.remoting.rmi.RmiRegistryFactoryBean;
    
    @Configuration
    public class ConfigureRMI {
    
        @Value("${jmx.rmi.password.file:/tmp/jmxremote.password}")
        private String passwordFile;
    
        @Value("${jmx.rmi.access.file:/tmp/jmxremote.access}")
        private String accessFile;
    
        @Value("${jmx.rmi.port:19999}")
        private Integer rmiPort;
    
        @Bean
        public RmiRegistryFactoryBean rmiRegistry() {
            final RmiRegistryFactoryBean rmiRegistryFactoryBean = new RmiRegistryFactoryBean();
            rmiRegistryFactoryBean.setPort(rmiPort);
            rmiRegistryFactoryBean.setAlwaysCreate(true);
            return rmiRegistryFactoryBean;
        }
    
        @Bean
        @DependsOn("rmiRegistry")
        public ConnectorServerFactoryBean connectorServerFactoryBean() throws Exception {
            final ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();
            connectorServerFactoryBean.setObjectName("connector:name=rmi");
            Map<String, Object> properties = new HashMap<>();
            properties.put("jmx.remote.x.password.file", passwordFile);
            properties.put("jmx.remote.x.access.file", accessFile);
            connectorServerFactoryBean.setEnvironmentMap(properties);
            connectorServerFactoryBean.setServiceUrl(String.format("service:jmx:rmi:///jndi/rmi://:%s/jmxrmi", rmiPort));
            return connectorServerFactoryBean;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-10-25
      • 1970-01-01
      • 1970-01-01
      • 2011-12-05
      • 1970-01-01
      • 1970-01-01
      • 2020-11-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多