Java:JMX service URL使用说明

在项目中经常用到JMX的service url,一直都没有完全理解它的用法,下面本文以RMI方式为例,分别从JMX server和JMX client两方面入手,简单的说明一下:

service:jmx:rmi://hostname1:port1/jndi/rmi://hostname2:port2/jmxrmi

比如有以上url,JMX server和JMX client都会用到这个URL,但它针对JMX server和JMX client含义是有区别的,在继续本文前,将JMX的处理流程讲一下:

1.JMX server会向RMI Registry注册服务,并在RMI Registry中存放RMI存根(RMI存根中存放有JMX服务的hostname和port)

2.JMX client会到RMI Registry上查询服务,如果查找到,则将RMI存根传给JMX client

3.JMX client拿到RMI存根后,就会使用它连上JMX server进行通讯(或者称作RMI方法调用)

对于JMX server

service:jmx:rmi

表示JMX服务使用JRMP协议进行通讯,JRMP协议是RMI通讯的标准协议

hostname1

这是一个不会被用到的hostname,就算是写了也会被忽略掉,所以一般不用写。如果hostname没有用,这里就出现了两个问题:

1.JMX client怎么知道JMX服务的连接地址?

2.如果JMX server在代理/防火墙后,或者JMX server有多个网卡,那么JMX client怎么才能知道如何与JMX服务通讯?

对于1,RMI Registry一般会将JMX服务的默认hostname或者ip传给JMX client

对于2,JMX server提供了一个系统参数指定代理/防火墙的hostname/IP,或者可访问的网卡的hostname/IP,指定方式如下:

-Djava.rmi.server.hostname=proxy_hostname,此时,RMI Registry会将这个hostname/IP传给JMX client

port1

这是JMX server提供的用于通讯的端口,如果不写,JMX server会随机分配一个;这个端口会在JMX client访问RMI Registry后返回给JMX client,JMX client拿到这个端口就会使用此端口与JMX server正式通讯了。如果JMX client需要通过代理/防火墙访问JMX server,这个端口也是代理/防火墙的端口。

jndi

使用jndi服务对JMX服务进行注册

rmi://hostname2:port2

说明要注册到RMI Registry上,RMI Registry的访问地址为rmi://hostname2:port2。其中hostname2:port2是RMI Registry所在的主机名和端口号,由于RMI Registry只可能与JMX server在同一台机器上,则hostname2只能是JMX server的主机名(或者其中一个主机名)或本机IP(或者其中一个本机IP)。

jmxrmi

RMI Registry中注册的服务名叫jmxrmi;jmxrmi也是JMX服务的默认注册名;用户可以设置自己的注册名

这里有一个令人疑惑的地方,怎么是用jndi api去注册JMX的服务呀?我们知道rmi服务一般是通过rmi api注册的,比如:

Naming.bind("rmi://hostname2:port2/jmxrmi", new Stub());

原来,jndi api可以适配到rmi api做注册,只要提供rmi的ContextFactory就行,上面的代码等价于:

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, "rmi://hostname2:port2/");
Context ictx = new InitialContext(env);
ictx.bind("jmxrmi", new Stub());

对于JMX client

service:jmx:rmi

表示将使用JRMP协议进行通讯

hostname1:port1

对JMX client没有任何用处,因为要访问的JMX服务的hostname和port都是RMI Registry返回的,所以一般都不写

jndi

JMX client使用jndi服务去查找JMX服务

rmi://hostname2:port2/jmxrmi

jndi服务内部是到RMI Registry上去查找服务的,RMI Registry的访问地址是hostname2:port2,查找服务的注册名为jmxrmi;当查找成功后,返回信息中会包含JMX服务对外的hostname和port,JMX client使用这个hostname和port就可以和JMX server上的JMX服务通讯,进行RMI方法调用了。

JMX client例子

import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class Client {

    public static void main(String[] args) throws Exception {
        // Create an RMI connector client and
        // connect it to the RMI connector server    
        JMXServiceURL url = 
            new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:1099/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url, null);

        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

        ObjectName mbeanName = new ObjectName("com.example:type=Hello");

        // Create a dedicated proxy for the MBean instead of
        // going directly through the MBean server connection
        HelloMBean mbeanProxy = 
            JMX.newMBeanProxy(mbsc, mbeanName, HelloMBean.class, true);

        mbeanProxy.setCacheSize(150);

        mbeanProxy.sayHello();

        jmxc.close();
    }
}

JMX server例子

import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

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 Main {

    public static void main(String[] args) throws Exception {
        int rmiPort = 1099;

        Registry registry = LocateRegistry.createRegistry(rmiPort);
        // Get the Platform MBean Server
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

        // Construct the ObjectName for the Hello MBean we will register
        ObjectName mbeanName = new ObjectName("com.example:type=Hello");

        // Create the Hello World MBean
        Hello mbean = new Hello();

        // Register the Hello World MBean
        mbs.registerMBean(mbean, mbeanName);

        String url = "service:jmx:rmi://localhost:1010/jndi/rmi://localhost:" + rmiPort + "/jmxrmi";

        JMXServiceURL jmxUrl = new JMXServiceURL(url);
        JMXConnectorServer jmxConnServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxUrl, null, mbs);
        
        jmxConnServer.start();   
        }
}

JMX常用参数

如果要使用JVM默认提供的JMX服务,并且关闭认证和SSL连接安全功能,则需要设置如下3项:

# 设置远程访问端口
com.sun.management.jmxremote.port=portNum
# 关闭认证功能
com.sun.management.jmxremote.authenticate=false
# 关闭SSL
com.sun.management.jmxremote.ssl=false

如果自己开发程序创建JMX服务,如“JMX server例子”中那样,就不需要使用以上参数,否则会报错。

其它有用的参数如下:

# 客户端多长时间接收不到响应就超时
sun.rmi.transport.tcp.responseTimeout=5000

# 客户端checker线程发起心跳请求的时间间隔,如果为0则表示关闭心跳线程
jmx.remote.x.client.connection.check.period=5000

# 服务器端Timeout线程超时时间
jmx.remote.x.server.connection.timeout=30000

参考文献:

Monitoring and Management Using JMX Technology:
http://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html

JMX tutorial:
https://docs.oracle.com/javase/8/docs/technotes/guides/jmx/tutorial/tutorialTOC.html

Java RMI:
https://docs.oracle.com/javase/8/docs/technotes/guides/rmi/

sun.rmi Properties:
https://docs.oracle.com/javase/8/docs/technotes/guides/rmi/sunrmiproperties.html

java.rmi Properties:
https://docs.oracle.com/javase/8/docs/technotes/guides/rmi/javarmiproperties.html

jndi-rmi
http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-rmi.html

你可能感兴趣的:(Java,JavaEE)