【问题标题】:How to set JMX remote port system environment parameters through java code for remote monitoring?如何通过java代码设置JMX远程端口系统环境参数进行远程监控?
【发布时间】:2011-11-08 18:21:41
【问题描述】:

我有一个程序需要动态(即在运行时)打开可用套接字并在其上启动 JMX 代理。此 JMX 参数是在 Java 代码中设置的,而不是通过命令行设置的。这工作正常。此后需要通过 Java Visual VM 进行监控(即发出 JMX 命令等) 远程

程序中的 RMI 服务器代理采用开箱即用的管理方式,如下所述: http://download.oracle.com/javase/6/docs/technotes/guides/management/agent.html

我的问题可以概括为: 这样的命令行属性如何设置到系统级别 通过Java代码,以便可以使用远程分析??

-Dcom.sun.management.jmxremote.port=1234

如果通过命令行设置“jmxremote.port”等参数, 远程监控工作正常。我正在尝试通过Java找到一种方法来做到这一点 而不是通过命令行。

程序不能通过命令行指定端口,因为新的可用端口必须在运行时计算出来。

该过程需要远程监控,并且在本地运行良好。 如果在命令行中没有指定以下参数,Java Visual VM 不会连接到进程。

-Dcom.sun.management.jmxremote.port=1234
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=10.0.0.128

我试过了。

System.setProperty("com.sun.management.jmxremote.port",Integer.toString(port));

这是在启动 JMXConnectorServer 之前在程序中完成的第一件事。不幸的是,它不被认可。 Java Visual VM 仅识别运行时属性(即通过命令行指定的 JMX 连接)。

还遇到了可以从java集合类中提取属性但无法跟踪属性“com.sun.management.jmxremote.port=”的方式

public static void setEnv(Map<String, String> newenv) throws Exception {
  Class[] classes = Collections.class.getDeclaredClasses();
  Map<String, String> env = System.getenv();

  for(Class cl : classes) {

    if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {

      Field field = cl.getDeclaredField("m");
      field.setAccessible(true);

      Object obj = field.get(env);
      Map<String, String> map = (Map<String, String>) obj;

      //map.clear();
      map.putAll(newenv);
    }
  }
}

任何帮助将不胜感激!

【问题讨论】:

  • 您应该编辑您的原始问题并包含您在 cmets 中发布的代码示例。

标签: java rmi jmx


【解决方案1】:

kbec 的回答指出了方法,但对我不起作用 - 但是通过查看 this post,我能够对其进行修改并获得有效的解决方案。

public static String loadJMXAgent(int port) throws IOException,
        AttachNotSupportedException, AgentLoadException,
        AgentInitializationException {
    String name = ManagementFactory.getRuntimeMXBean().getName();
    VirtualMachine vm = VirtualMachine.attach(name.substring(0,
            name.indexOf('@')));

    String lca = vm.getAgentProperties().getProperty(
            "com.sun.management.jmxremote.localConnectorAddress");
    if (lca == null) {
        Path p = Paths.get(System.getProperty("java.home")).normalize();
        if (!"jre".equals(p.getName(p.getNameCount() - 1).toString()
                .toLowerCase())) {
            p = p.resolve("jre");
        }
        File f = p.resolve("lib").resolve("management-agent.jar").toFile();
        if (!f.exists()) {
            throw new IOException("Management agent not found");
        }
        String options = String.format("com.sun.management.jmxremote.port=%d, " +
                "com.sun.management.jmxremote.authenticate=false, " +
                "com.sun.management.jmxremote.ssl=false", port);
        vm.loadAgent(f.getCanonicalPath(), options);
        lca = vm.getAgentProperties().getProperty(
                "com.sun.management.jmxremote.localConnectorAddress");
    }
    vm.detach();
    return lca;
}

这在 Eclipse 中有效,但是让它在命令行中工作是另一回事 - 这里有一些讨论 Why does using the Java Attach API fail on linux? (even though maven build completes) 但我发现将 $JAVA_HOME/lib/tools.jar 添加到我的类路径解决了这个问题。

【讨论】:

  • “com.sun.management.jmxremote.localConnectorAddress”属性未创建。但这段代码对我有用
【解决方案2】:

您的做法有点错误。当您的代码被调用时,您已经错过了让这些属性发挥作用的机会。

您需要创建一个 RmiRegistry,然后创建一个链接到平台 MBeanServer 的 JMXConnectorServer,如下所示:

private void createJmxConnectorServer() throws IOException {
    LocateRegistry.createRegistry(1234);
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost/jndi/rmi://localhost:1234/jmxrmi");
    JMXConnectorServer svr = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
    svr.start();
}

【讨论】:

    【解决方案3】:

    如果您未指定任何 jmxremote env 作为运行参数,则未加载 JMX 管理代理。你可以试试这个动态加载:

    public static String loadJMXAgent(int port) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
        System.setProperty("com.sun.management.jmxremote.port", Integer.toString(port));
        String name = ManagementFactory.getRuntimeMXBean().getName();
        VirtualMachine vm = VirtualMachine.attach(name.substring(0, name.indexOf('@')));
    
        String lca = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
        if (lca == null) {
            Path p = Paths.get(System.getProperty("java.home")).normalize();
            if (!"jre".equals(p.getName(p.getNameCount()-1).toString().toLowerCase())) p = p.resolve("jre");
            File f = p.resolve("lib").resolve("management-agent.jar").toFile();
            if (!f.exists()) throw new IOException("Management agent not found");
    
            vm.loadAgent(f.getCanonicalPath(), "com.sun.management.jmxremote");
            lca = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
        }
        vm.detach();
        return lca;
    }
    

    您必须包含jdk/lib/tools.jar

    【讨论】:

      【解决方案4】:

      我尝试了一些方法来从 java 代码中指定 jmxremote 端口,以便在特定端口上建立连接,并得出以下结论:

      如果指定了 jmxremote arg: 平台 mbean 服务器 由 JVM 启动,在我的代码修改必要的 jmxremote System.properties 之前。每个 mbean 服务器都有自己的 bean 注册表。平台和 JVM mbean 无法通过其他方式向其注册自己的 bean。

      您可以在设置 jmx 端口属性后创建备用 mbean 服务器。这将侦听您指定的正确 jmx 端口。

      这样选择平台服务器:

      MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
      

      这样你自己的

      System.setProperty("com.sun.management.jmxremote.port","9991");
      //...
      MBeanServer mbsCustom=MBeanServerFactory.createMBeanServer();
      

      还要考虑到 linux 有它的 环回接口,所以你应该明确指定正确的主机名来监听。

      根据手册,不建议使用平台以外的其他 MBeanServer,但我可以想象在某些情况下命令行选项不是启动服务器的方式。 p>

      【讨论】:

        【解决方案5】:

        这对我有用。参考Oracle JMX Tutorial。我假设您已经知道如何正确使用以下示例中使用的 SimpleMXBean。

        package sample;
        
        import java.io.IOException;
        import java.lang.management.ManagementFactory;
        import java.rmi.registry.LocateRegistry;
        import java.util.HashMap;
        import java.util.Map;
        
        import javax.management.MBeanServer;
        import javax.management.ObjectName;
        import javax.management.remote.JMXConnectorServer;
        import javax.management.remote.JMXConnectorServerFactory;
        import javax.management.remote.JMXServiceURL;
        
        public class MBServerTest {
            public static void loadJMXAgent(int port, MBeanServer mbs) throws IOException  {
                LocateRegistry.createRegistry(port);
                System.out.println("Initialize the environment map");
                Map<String,Object> env = new HashMap<String,Object>();
                env.put("com.sun.management.jmxremote.authenticate", "false");
                env.put("com.sun.management.jmxremote.ssl", "false");
                System.out.println("Create an RMI connector server");
                JMXServiceURL url =
                    new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:"+port+"/jmxrmi");
                JMXConnectorServer cs =
                    JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
        
                // Start the RMI connector server.
                //
                System.out.println("Start the RMI connector server");
                cs.start();
        
            }
        
            public static void main(String[] args) throws Exception {
                MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
                loadJMXAgent(1199,mbs);
        
                SimpleStandard cache = new SimpleStandard();
        
                ObjectName name = new ObjectName(
                        "org.javalobby.tnt.jmx:type=ApplicationCacheMBean");
                mbs.registerMBean(cache, name);
                imitateActivity(cache);
            }
        
            private static void imitateActivity(SimpleStandard cache) {
                while (true) {
                    try {
                        cache.cacheObject("hello");
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }
        

        【讨论】:

          【解决方案6】:

          System.setProperty()-D 命令行选项相同。但是很明显,您必须尽早调用它,以便在读取该属性之前对其进行设置。

          【讨论】:

          • 分析器正在读取的用于远程分析的属性是在 JVM 启动时设置的默认属性。在这种情况下,当 java 代码启动时。我很晚才启动 java visual vm profiler 并在启动 JMXConnectorServer 之前设置属性。试图弄清楚如何覆盖该属性,使其反映为 jmx 系统级别并且可以被 java visual vm 检测到?希望有任何关于如何称其为“足够早”的建议。
          • 在没有任何-Dcom.sun.management.jmxremote 的情况下运行应用程序并稍后在运行时设置此环境是个坏主意,因为未加载 JMX 代理,因此无法使用。
          猜你喜欢
          • 1970-01-01
          • 2015-07-16
          • 1970-01-01
          • 1970-01-01
          • 2017-02-15
          • 1970-01-01
          • 2020-12-17
          • 2011-06-13
          • 1970-01-01
          相关资源
          最近更新 更多