閱讀160 返回首頁    go 阿裏雲 go 技術社區[雲棲]


不用重新配置,用jconsole連接遠程機器進程及獲得本地進程的JMX Url的終極辦法

估計有很多人都在想用jconsole連接遠程機器上的進程時,發現沒有配置jmx端口,或者其它的東東。

下麵介始一種很簡單的辦法,可以不用重啟遠程機器的進程:

ssh -X  192.168.66.66  -l username


連接上去之後,可以直接運行jconsole進程,然後在本機就會彈出一個jconsole的窗口了。

實際上這個不是用jconsole連接遠程機器的進程,而是把遠程機器上的X輸出轉地本地來。

如果有提示失敗,那麼可能要配置下ssh可以轉發X。


=====================================

但是如果是想用編程的方式去連接本地的Java進程,而又不能更改配置重啟。

比如你有個程序是用jmx的方式去得到監控數據的,那麼肯定不能重啟目標進程,如druid的StatViewServlet:

https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE


可以用下麵的方法來得到本地進程的jmx url。

在以前的博客《查找本地進程的jmx url的代碼》裏有提到ActiveMQ裏獲得本地進程的jmx url的方法。

https://blog.csdn.net/hengyunabc/article/details/8938281

這個方法有時卻不起效,得到的是null,但是用jconsole卻又能連接。於是研究了下jconsole的源代碼,終於發現,原來jconsole在得不到目標進程的"com.sun.management.jmxremote.localConnectorAddress"環境變量值時,會先嚐試讓目標進程加載management-agent.jar,這樣就可以得到jmx url了。

jconsole的相關源代碼OpenJDK源代碼下麵的 jdk/src/share/classes/sun/tools/jconsole/LocalVirtualMachine.java 裏。可以在這裏直接看到:

https://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/classes/sun/tools/jconsole/LocalVirtualMachine.java


下麵是改進後的得到本地進程jmx url的代碼,對於異常的處理不是很完善,不過不影響使用:

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.Properties;

public class AbstractJmxCommand  {
    private static final String CONNECTOR_ADDRESS =
        "com.sun.management.jmxremote.localConnectorAddress";

    public static String getJVM() {
        return System.getProperty("java.vm.specification.vendor");
    }

    public static boolean isSunJVM() {
        // need to check for Oracle as that is the name for Java7 onwards.
        return getJVM().equals("Sun Microsystems Inc.") || getJVM().startsWith("Oracle");
    }
    
    public static void main(String[] args) {
		if (args == null || args.length == 0) {
			System.out.println("Usage: pid");
			return;
		}
		int pid = Integer.parseInt(args[0]);
		System.out.println(new AbstractJmxCommand().findJMXUrlByProcessId(pid));
	}
    /**
     * Finds the JMX Url for a VM by its process id
     *
     * @param pid
     * 		The process id value of the VM to search for.
     *
     * @return the JMX Url of the VM with the given pid or null if not found.
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected String findJMXUrlByProcessId(int pid) {

        if (isSunJVM()) {
            try {
                // Classes are all dynamically loaded, since they are specific to Sun VM
                // if it fails for any reason default jmx url will be used

                // tools.jar are not always included used by default class loader, so we
                // will try to use custom loader that will try to load tools.jar

                String javaHome = System.getProperty("java.home");
                String tools = javaHome + File.separator +
                        ".." + File.separator + "lib" + File.separator + "tools.jar";
                URLClassLoader loader = new URLClassLoader(new URL[]{new File(tools).toURI().toURL()});

                Class virtualMachine = Class.forName("com.sun.tools.attach.VirtualMachine", true, loader);
                Class virtualMachineDescriptor = Class.forName("com.sun.tools.attach.VirtualMachineDescriptor", true, loader);

                Method getVMList = virtualMachine.getMethod("list", (Class[])null);
                Method attachToVM = virtualMachine.getMethod("attach", String.class);
                Method getAgentProperties = virtualMachine.getMethod("getAgentProperties", (Class[])null);
                Method getVMId = virtualMachineDescriptor.getMethod("id",  (Class[])null);

                List allVMs = (List)getVMList.invoke(null, (Object[])null);

                for(Object vmInstance : allVMs) {
                    String id = (String)getVMId.invoke(vmInstance, (Object[])null);
                    if (id.equals(Integer.toString(pid))) {

                        Object vm = attachToVM.invoke(null, id);

                        Properties agentProperties = (Properties)getAgentProperties.invoke(vm, (Object[])null);
                        String connectorAddress = agentProperties.getProperty(CONNECTOR_ADDRESS);

                        if (connectorAddress != null) {
                            return connectorAddress;
                        } else {
                            break;
                        }
                    }
                }
                
                //上麵的嚐試都不成功,則嚐試讓agent加載management-agent.jar
                Method getSystemProperties = virtualMachine.getMethod("getSystemProperties", (Class[])null);
                Method loadAgent = virtualMachine.getMethod("loadAgent", String.class, String.class);
                Method detach = virtualMachine.getMethod("detach", (Class[])null);
                for(Object vmInstance : allVMs) {
                    String id = (String)getVMId.invoke(vmInstance, (Object[])null);
                    if (id.equals(Integer.toString(pid))) {

                        Object vm = attachToVM.invoke(null, id);

                        Properties systemProperties = (Properties)getSystemProperties.invoke(vm, (Object[])null);
                        String home = systemProperties.getProperty("java.home");
                        
                        // Normally in ${java.home}/jre/lib/management-agent.jar but might
                        // be in ${java.home}/lib in build environments.

                        String agent = home + File.separator + "jre" + File.separator +
                                           "lib" + File.separator + "management-agent.jar";
                        File f = new File(agent);
                        if (!f.exists()) {
                            agent = home + File.separator +  "lib" + File.separator +
                                        "management-agent.jar";
                            f = new File(agent);
                            if (!f.exists()) {
                                throw new IOException("Management agent not found");
                            }
                        }
                        
                        agent = f.getCanonicalPath();
                        
                        loadAgent.invoke(vm, agent, "com.sun.management.jmxremote");
                        
                        Properties agentProperties = (Properties)getAgentProperties.invoke(vm, (Object[])null);
                        String connectorAddress = agentProperties.getProperty(CONNECTOR_ADDRESS);

                        //detach 這個vm
                        detach.invoke(vm, (Object[])null);
                        
                        if (connectorAddress != null) {
                            return connectorAddress;
                        } else {
                            break;
                        }
                    }
                }
            } catch (Exception ignore) {
            	System.err.println(ignore);
            }
        }

        return null;
    }
}


最後更新:2017-04-03 14:54:32

  上一篇:go Java之dead code——無用代碼
  下一篇:go Java中的隨機數生成器:Random,ThreadLocalRandom,SecureRandom