读书人

JMX(6)Remoting

发布时间: 2012-07-22 19:31:17 作者: rapoo

JMX(六)Remoting

1 Introduction
??? Java Management Extension (JMX) API定义于JSR 3,用于应用程序管理。这些API对于被管理的应用程序来说是本地的。也就是说,在JSR 160发布之前,如果客户端要通过JMX来管理和监控远程的应用程序,并没有标准的做法。JSR 160扩展了JSR 3,提供了标准的API用于连接到支持JMX的远程应用程序。JSR 255将会把JMX升级到2.0,并可能体现在Java 7中。
??? 目前JSR 160定义了基于RMI(支持RMI/JRMP 和 RMI/IIOP)的连接器,还定义了可选的JMXMP连接器,它基于TCP Socket和Java序列化机制。

?

2 JMX Remoting API
2.1 JMXServiceURL
??? JMXServiceURL用于标识JMXConnectorServer,它是采用以下形式的字符串:
??? service:jmx:<protocol>://[[[ <host>]: <port>]/ <path>]
??? "protocol" 指定了协议,例如:rmi、iiop、jmxmp 或者 soap。"host"、"port" 和 "path"是可选的。JMXServiceURL并不足以描述所有的用于连接到JMXConnectorServer的配置信息(例如配置RMIClientSocketFactory 和RMIServerSocketFactory),因此在构造JMXConnectorServer 和 JMXConnector的时候,还可能需要通过一个Map实例指定其它属性。

?

2.2 JMXConnectorServer
??? JMXConnectorServer 是MBeanServer端的组件,它需要被关联到MBeanServer:可以通过在创建JMXConnectorServer的时候显式指定MBeanServer;也可以把JMXConnectorServer(本身是一个MBean)注册到MBeanServer中。
??? 在被关联到MBeanServer之后,需要启动JMXConnectorServer以处理客户端的请求。同样,如果希望停止处理客户端的请求,那么需要停止JMXConnectorServer。JMXConnectorServer被停止之后,不应该试图重新启动它,而是应该将其丢弃。
??? 最好通过JMXConnectorServerFactory来创建JMXConnectorServer,例如:

Java代码??JMX(6)Remoting
  1. //?The?address?of?the?connector?server??
  2. JMXServiceURL?address?=?new?JMXServiceURL("service:jmx:rmi://host");??
  3. ??
  4. //?The?environment?map,?null?in?this?case??
  5. Map?environment?=?null;??
  6. ??
  7. //?The?MBeanServer?to?which?the?JMXConnectorServer?will?be?attached?to??
  8. MBeanServer?server?=?MBeanServerFactory.createMBeanServer();??
  9. ??
  10. //?Create?the?JMXCconnectorServer??
  11. JMXConnectorServer?cntorServer?=?JMXConnectorServerFactory.newJMXConnectorServer(address,?environment,?server);??
  12. ??
  13. //?Start?the?JMXConnectorServer??
  14. cntorServer.start();??

??? 在以上的例子中,创建JMXConnectorServer的时候指定了MBeanServer。以下是一个将JMXConnectorServer作为MBean注册到MBeanServer的例子:

Java代码??JMX(6)Remoting
  1. //?The?address?of?the?connector??
  2. JMXServiceURL?address?=?new?JMXServiceURL("service:jmx:rmi://host");??
  3. ??
  4. //?The?environment?map,?null?in?this?case??
  5. Map?environment?=?null;??
  6. ??
  7. JMXConnectorServer?cntorServer?=?JMXConnectorServerFactory.newJMXConnectorServer(address,?environment,?null);??
  8. ??
  9. //?The?MBeanServer?to?which?the?JMXConnectorServer?will?be?registered?in??
  10. MBeanServer?server?=?MBeanServerFactory.createMBeanServer();??
  11. ??
  12. //?Register?the?JMXConnectorServer?in?the?MBeanServer??
  13. ObjectName?cntorServerName?=?ObjectName.getInstance("connectors:protocol=rmi");??
  14. server.registerMBean(cntorServer,?cntorServerName);??
  15. ??
  16. //?Start?the?JMXConnectorServer??
  17. cntorServer.start();??
  18. ??
  19. //?An?alternative?way?to?start?the?JMXConnectorServer?via?the?MBeanServer??
  20. server.invoke(cntorServerName,?"start",?null,?null);??
  21. ??
  22. //?Yet?another?way?to?start?the?JMXConnectorServer?via?the?MBeanServer??
  23. Object?proxy?=?MBeanServerInvocationHandler.newProxyInstance(server,?cntorServerName,?JMXConnectorServerMBean.class,?true);??
  24. JMXConnectorServerMBean?cntorServerMBean?=?(JMXConnectorServerMBean)proxy;??
  25. cntorServerMBean.start();??

??? 需要注意的是,在这个例子中使用了三种不同的方式启动JMXConnectorServer:第一种方式是直接调用cntorServer对象的start方法;第二种方式是通过MBeanServer调用MBean的方法;第三种方式是使用了MBeanServerInvocationHandler,它继承自InvocationHandler,用于从MBean的管理接口向MBeanServer中的 MBean 转发方法调用。当调用从newProxyInstance()方法返回的代理对象上的toString()、hashCode() 和equals(Object)等方法时,这些方法调用同样也会被转发到MBeanServer中的MBean ,因此当且仅当 MBean 在其管理接口中声明了这些方法时才能如此操作,否则会抛出异常。

?

2.3 JMXConnector
?? JMXConnector是客户端的组件,客户端程序通过它连接到远程的MBeanServer。客户端可以通过JMXConnector获得远程MBeanServer的MBeanServerConnection 接口,并以类似本地的方式使用它。JMXConnector还支持在远程的MBeanServer上注册本地或者远程的通知监听器,以接收来自远程MBeanServer的通知。
??? 最好通过JMXConnectorFactory来创建JMXConnector,例如:

Java代码??JMX(6)Remoting
  1. //?The?address?of?the?connector?server??
  2. JMXServiceURL?address?=?...;??
  3. ??
  4. //?The?environment?map,?null?in?this?case??
  5. Map?environment?=?null;??
  6. ??
  7. //?Create?the?JMXCconnectorServer??
  8. JMXConnector?cntor?=?JMXConnectorFactory.connect(address,?environment);??
  9. ??
  10. //?Obtain?a?"stub"?for?the?remote?MBeanServer??
  11. MBeanServerConnection?mbsc?=?cntor.getMBeanServerConnection();??
  12. ??
  13. //?Call?the?remote?MBeanServer??
  14. String?domain?=?mbsc.getDefaultDomain();??
  15. 也可以先实例化JMXConnector,然后在连接到MBeanServer,例如:??
  16. //?The?address?of?the?connector?server??
  17. JMXServiceURL?address?=?...;??
  18. ??
  19. //?The?creation?environment?map,?null?in?this?case??
  20. Map?creationEnvironment?=?null;??
  21. ??
  22. //?Create?the?JMXCconnectorServer??
  23. JMXConnector?cntor?=?JMXConnectorFactory.newJMXConnector(address,?creationEnvironment);??
  24. ??
  25. //?The?connection?environment?map,?null?in?this?case??
  26. //?May?contain?-?for?example?-?user's?credentials??
  27. Map?connectionEnvironment?=?null;??
  28. ??
  29. //?Connect??
  30. cntor.connect(connectionEnvironment);??
  31. ??
  32. //?Obtain?a?"stub"?for?the?remote?MBeanServer??
  33. MBeanServerConnection?mbsc?=?cntor.getMBeanServerConnection();??
  34. ??
  35. //?Call?the?remote?MBeanServer??
  36. String?domain?=?mbsc.getDefaultDomain();??

??? 需要注意的是,以上的例子中采用了两个不同的Map对象保存相关的属性:一个用于实例化;另外一个用于连接。MBeanServerConnection接口在JMX 1.2中定义,用于支持remote API。由于之前版本的MBeanServer接口不能直接被客户端使用(例如其registerMBean(), deserialize(), getClassLoader()等方法对于客户端来说没有意义),因此在JMX 1.2中,MBeanServer接口继承自MBeanServerConnection接口。

??? RMIConnector是标准的JMXConnector。由于它使用RMI协议,因此需要一个stub object来处理RMI调用的细节。stub class通常存在于客户端的类路径上,但是stub object通常是从命名服务器处下载,因此RMI客户端需要知道stub object被绑定到RMI server上的路径。JSR 160定义了两种获得stub object的方式:
??? 第一种方式不需要命名服务器,而是将stub object编码为JMXServiceURL的一部分,即"encoded form"。如果采用RMI/JRMP协议,那么对stub object被序列化之后的所有字节进行Base64编码,然后将编码后的字符添加到JMXServiceURL中,并且以/stub/开始;如果采用RMI/ IIOP,那么IOR被添加到JMXServiceURL中,并以/ior/开始。以下是个例子:

Java代码??JMX(6)Remoting
  1. //?JRMP?encoded?form??
  2. service:jmx:rmi://localhost/stub/rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LnJlbW90ZS5ybWkuUk1JU2VydmVySW1wbF9TdHViAAAAAAAAAAICAAB4cgAaamF2YS5ybWkuc2VydmVyLlJlbW90ZVN0dWLp/tzJi+FlGgIAAHhyABxqYXZhLnJtaS5zZXJ2ZXIuUmVtb3RlT2JqZWN002G0kQxhMx4DAAB4cHc5AAtVbmljYXN0UmVmMgAADjE2LjE5Mi4xMjAuMTI5AAANcQAAAAAAAAAIAOgIQgAAAPlXjcJugAEAeA==??
  3. ??
  4. //?IIOP?encoded?form??
  5. service:jmx:iiop://localhost/ior/IOR:000000000000003b524d493a6a617661782e6d616e6167656d656e742e72656d6f74652e726d692e524d495365727665723a303030303030303030303030303030300000000000010000000000000068000102000000000f31362e3139322e3132302e31323900000d93000000000019afabcb0000000002578f08b80000000800000000000000000a0000000000000100000001000000200000000000010001000000020501000100010020000101090000000100010100??

??? 第二种方式是在JMXServiceURL中指定命名服务器和stub object被绑定到的JNDI路径,即"JNDI form"。在JMXServiceURL中以/jndi/开始。将JNDI的相关配置传递给RMIConnector的方式有多种。例如URL 'rmi://namingHost:1099/jndiPath'指明命名服务器在名为'namingHost'的主机上并监听1099端口,命名服务器是rmiregistry,路径是'/jndiPath'。同样URL 'iiop://namingHost:900/jndiPath'指明命名服务器在名为'namingHost'的主机上并监听900端口,命名服务器是COS命名服务,路径是'/jndiPath'。以下是个例子:

Java代码??JMX(6)Remoting
  1. //?JNDI?URL?form?for?JRMP??
  2. service:jmx:rmi://rmiHost/jndi/rmi://namingHost:1099/jndiPath??
  3. ??
  4. //?JNDI?URL?form?for?IIOP??
  5. service:jmx:iiop://rmiHost/jndi/iiop://namingHost:900/jndiPath??

??? 此外,JNDI属性也可以通过系统属性、类路径上的'jndi.properties'文件、在调用JMXConnector.connect(Map environment)或者JMXConnectorFactory.connect(JMXServiceURL url, Map environment)方法时传递的environment参数等不同方式指定。如果JNDI属性通过这些方式指定,那么JMXServiceURL中可以使用简短的JNDI格式,即只指定JNDI path。以下是个例子:

Java代码??JMX(6)Remoting
  1. //?JNDI?short?form?for?JRMP??
  2. service:jmx:rmi://rmiHost/jndi/jndiPath??
  3. ??
  4. //?JNDI?short?form?for?IIOP??
  5. service:jmx:iiop://rmiHost/jndi/jndiPath??
  6. ??
  7. //?Code?example?that?uses?the?JNDI?short?form?specifying?JNDI?properties?via?environment?Map??
  8. //?The?JNDI?environment??
  9. Map?environment?=?new?HashMap();??
  10. environment.put(Context.INITIAL_CONTEXT_FACTORY,?"com.sun.jndi.rmi.registry.RegistryContextFactory");??
  11. environment.put(Context.PROVIDER_URL,?"rmi://namingHost:1099");??
  12. //?The?JNDI?short?form?JMXServiceURL??
  13. JMXServiceURL?url?=?new?JMXServiceURL("service:jmx:rmi://rmiHost/jndi/jndiPath");??
  14. //?Connect?to?the?server?side??
  15. JMXConnector?connector?=?JMXConnectorFactory.connect(url,?environment);??

?

2.4 Remote Notification
??? JSR 160定义的连接器可以接收到远程MBean发送的通知。通知被传递的细节依赖于连接器使用的协议。要接收通知,客户端必须要通过调用MBeanServerConnection.addNotificationListener(...)方法来注册一个监听器,这个方法有两个重载的版本:
??? 第一个版本接受一个ObjectName实例作为监听器,由于监听器是远程MBeanServer中的一个MBean,因此方法中的filter和handback参数必须被传递到远程的MBeanServer,因此它们必须是可以被序列化的。
??? 另一个版本接受NotificationListener的实例作为监听器。这个监听器是客户端的本地对象,而且监听器对象不会被传递到远程的MBeanServer,因此它也不必是可以被序列化的。NotificationFilter是否被传递到远程的MBeanServer取决于connector使用的协议,handback对象不会被传递到远程MBeanServer。在另一方面,远程的MBean发送的通知必须是可以被序列化的,因为通知会被传递到客户端。以下是在远程的MBean上注册监听器的例子:

Java代码??JMX(6)Remoting
  1. //?The?address?of?the?connector?server??
  2. JMXServiceURL?address?=?...;??
  3. ??
  4. //?The?JMXConnector??
  5. JMXConnector?connector?=?JMXConnectorFactory.connect(address);??
  6. ??
  7. //?The?MBeanServerConnection?"stub"??
  8. MBeanServerConnection?mbsc?=?connector.getMBeanServerConnection();??
  9. ??
  10. //?The?MBeanServerDelegate?emits?notifications?about?registration/unregistration?of?MBeans??
  11. ObjectName?delegateName?=?ObjectName.getInstance("JMImplementation:type=MBeanServerDelegate");??
  12. ??
  13. NotificationListener?listener?=?new?NotificationListener()??
  14. {??
  15. ???public?void?handleNotification(Notification?notification,?Object?handback)??
  16. ???{??
  17. ??????//?Do?something??
  18. ???}??
  19. };??
  20. ??
  21. mbsc.addNotificationListener(delegateName,?listener,?null,?null);??

?

3 JMX Remoting Security
??? JSR 160提供了一种可插拔的认证机制,它基于JMXAuthenticator接口。JMXAuthenticator接口中只包含如下一个方法,它接受从客户端得到的身份证明信息作为参数,返回javax.security.auth.Subject对象的一个实例。

Java代码??JMX(6)Remoting
  1. public?Subject?authenticate(Object?credentials)?throws?SecurityException??

??? 以下是在JMXConnectorServer 中使用JMXAuthenticator的例子:

Java代码??JMX(6)Remoting
  1. //?The?address?of?the?connector?server??
  2. JMXServiceURL?address?=?...;??
  3. ??
  4. //?The?JMXAuthenticator?implementation??
  5. JMXAuthenticator?authenticator?=?...;??
  6. ??
  7. //?The?environment?map??
  8. Map?environment?=?new?HashMap();??
  9. environment.put(JMXConnectorServer.AUTHENTICATOR,?authenticator);??
  10. ??
  11. //?The?MBeanServer?to?which?the?JMXConnectorServer?will?be?attached?to??
  12. MBeanServer?server?=?MBeanServerFactory.createMBeanServer();??
  13. ??
  14. //?Create?the?JMXCconnectorServer??
  15. JMXConnectorServer?cntorServer?=?JMXConnectorServerFactory.newJMXConnectorServer(address,?environment,?server);??
  16. ??
  17. //?Start?the?JMXConnectorServer??
  18. cntorServer.start();??

??? 以下是在JMXConnector中使用JMXAuthenticator的例子:

Java代码??JMX(6)Remoting
  1. //?The?address?of?the?connector?server??
  2. JMXServiceURL?address?=?...;??
  3. ??
  4. //?Create?the?JMXCconnectorServer??
  5. JMXConnector?cntor?=?JMXConnectorFactory.newJMXConnector(address,?null);??
  6. ??
  7. //?The?credentials??
  8. Object?credentials?=?...;??
  9. ??
  10. //?The?connection?environment?map??
  11. Map?environment?=?new?HashMap();??
  12. environment.put(JMXConnector.CREDENTIALS,?credentials);??
  13. ??
  14. //?Connect?and?invoke?an?operation?on?the?remote?MBeanServer??
  15. try??
  16. {??
  17. ???cntor.connect(environment);??
  18. }??
  19. catch?(SecurityException?x)??
  20. {??
  21. ???//?Uh-oh?!?Bad?credentials?!??
  22. ???throw?x;??
  23. }??
  24. ??
  25. //?Obtain?a?stub?for?the?remote?MBeanServer??
  26. MBeanServerConnection?mbsc?=?cntor.getMBeanServerConnection();??
  27. ??
  28. //?Call?the?remote?MBeanServer??
  29. ObjectName?delegate?=?ObjectName.getInstance("JMImplementation:type=MBeanServerDelegate");??
  30. String?id?=?mbsc.getAttribute(delegate,?"MBeanServerId");??

??? 当调用从JMXConnector 得到的MBeanServerConnection 接口上的方法时,最终发生在远程MBeanServer的调用会以认证后的Subject 身份执行。如果使用SecurityManager,那么还可以为不同的Subject指定不同的权限。

?

读书人网 >软件架构设计

热点推荐