JMX Server Behind Firewall

本来JMX用起来很简单

mBeanServer = ManagementFactory.getPlatformMBeanServer();
Object obj=new MyMbean();
mBeanServer.registerMBean(obj, new ObjectName("com.tianshen:type=xiyou,name="+obj.getClass().getCanonicalName()));

但是JMX在远程使用的时候经常不大好使。

JMX remote最基本的用法是,在启动的时候加上

-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.password.file=jmxpass
-Dcom.sun.management.jmxremote.port=13000
-Dcom.sun.management.jmxremote.ssl=false

下面解释下com.sun.management.jmxremote.port这个东西。默认情况下,JMX协议是架构在RMI协议的基础上。那么RMI就需要一个位置定位服务,即rmiregistry 。rmiregistry 是一个类似于CORBA命令服务或者LDAP的东西。rmiregistry可以是一个单独的进程,带上端口号执行JDK/bin/rmiregistry即可。也可以是嵌入在当前的java进程中。而com.sun.management.jmxremote.port这个参数就是指,rmiregistry这个服务在哪个端口监听。

然后,JMX Server会向这个rmiregistry注册自己。注册的时候需要提供一个主机地址和一个端口号。而这个端口号,默认是随机生成的。这个主机地址,常常很悲剧的是127.0.0.1或localhost。由于端口号是随机生的,这让系统管理员很为难。而且,如果那个主机地址是localhost……

其实几年前在做mz项目的时候就遇到这个问题了,只不过今天又遇到了,拿出来分享下。

MBeanServer的初始化代码得改成这样:

final int port1=14000; //the port number on which the RMIServer and RMIConnection remote objects are exported
final int port2=15000; //the port number of the RMI Registry
LocateRegistry.createRegistry(port2);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://0.0.0.0:" + 
          port1  + "/jndi/rmi://:" + port2 + "/jmxrmi");
java.util.HashMap<String,Object> env = new java.util.HashMap<String,Object>();
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mBeanServer);        
cs.start();
//不要忘记调cs.stop();

用上面的代码,明确的指定端口号,就没有问题了。

Client代码如下:

HashMap env = new HashMap(); 
String[] credentials = new String[] { "controlRole" , "1234" }; 
env.put("jmx.remote.credentials", credentials); 
final String hostName="192.168.1.145";             
final int port1=14000; 
final int port2=15000; 
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://"+hostName+":" + 
      port1  + "/jndi/rmi://localhost:" + port2 + "/jmxrmi");       

JMXConnector jmxc = JMXConnectorFactory.connect(url, env); 
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
String domains[] = mbsc.getDomains(); 
for (int i = 0; i < domains.length; i++) { 
    System.out.println("Domain[" + i + "] = " + domains[i]); 
} 

jmxc.close();

用jconsole去连的时候,输入的端口号是上面的port2,也就是rmiregistry的端口。

另外,IP是127.0.0.1的原因是:它往RMI注册的时候需要获取本机IP,那么最传统的方式就是先用gethostname得到主机名,然后把主机名解析成IP。而很多Linux机器安装的时候没有正确的设置主机名,而是默认采用了localhost。用hostname改完主机名之后,不要忘记用 ping `hostname`测试下能不能ping通。

此博客中的热门博文

少写代码,多读别人写的代码

在windows下使用llvm+clang

tensorflow distributed runtime初窥