JMX RMI 访问

转自:http://blog.sina.com.cn/s/blog_6151984a0100mjia.html

  • RMI(Remote Method Invocation)
RMI是 不同JVM之间 的对象通信的协议

  • JMX RMI访问的基本步骤:
1 启动MBeanServer
2 建立并启动 NamingService MBean,实际就是rmiregistry
3 为MBeanServer建立160 JMX RMIConnector,此RMI连接器提供:
service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxconnector
4 客户端连接
4.1 通用客户端(如jconsole)
4.2 自定义客户端
生成 JMXConnector ,与JMI server建立 RMI连接
建立与MBean server的连接
MBeanServerInvocationHandler.newProxyInstance()获取代理MBean


  • JMX RMI例子
1. RMI Server
package com.machome.jmx.appTest;

import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

import com.machome.bean.Hello;

import mx4j.tools.naming.NamingService;


public class RmiServer
{
    private MBeanServer mBeanServer;
    private JMXConnectorServer connectorServer;
    private JMXServiceURL jmxUrl;
   
    public RmiServer() throws Exception{
          mBeanServer = MBeanServerFactory.createMBeanServer();
          createRmiregistry();
          CreateJMXConnector();
    }
   public static void main(String[] args) throws Exception
   {
     
       RmiServer rmiServer = new RmiServer();
       // 加一个测试MBean Hello对象
       rmiServer.addMbean(new Hello(), "mbean", "name", "hello");
       rmiServer.startJMXConnector();
      
       System.out.println("Server up and running");
   }
  
   public ObjectName addMbean(Object o,String domain,String key,String value) throws Exception{
          ObjectName oName = new ObjectName(domain, key, value);
          mBeanServer.registerMBean(o, oName);
          return oName;
}
  
  
   public void createRmiregistry() throws Exception{
         
          // 1.注册NamingService MBean
          ObjectName namingName = ObjectName
                      .getInstance("naming:type=rmiregistry");
          // 这里采用MC4J的mx4j.tools.naming.NamingService
          NamingService ns = new NamingService();
         
          mBeanServer.registerMBean(ns, namingName);
          // 2.启动NamingService MBean
          mBeanServer.invoke(namingName, "start", null, null);
   }
  
  
   public void CreateJMXConnector() throws Exception{

          // 1.nammingPort,从NamingService获得Port,缺省是1099
          ObjectName namingName = ObjectName.getInstance("naming:type=rmiregistry");
     int namingPort = ((Integer)mBeanServer.getAttribute(namingName, "Port")).intValue();
        
          // 2. jndiPath
          String jndiPath = "/jmxconnector";
          // 3. JMXServiceURL ,为:
          // service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxconnector
          jmxUrl = new JMXServiceURL("service:jmx:rmi://localhost/jndi/rmi://localhost:"
                                              + namingPort + jndiPath);
         
          // 4.Create and start the RMIConnectorServer
          // 中间设为null的参数,是针对认证的,我们这里没打开,设为null
             connectorServer = JMXConnectorServerFactory
                                  .newJMXConnectorServer(jmxUrl, null, mBeanServer);
   }
  
   public void startJMXConnector() throws Exception{
       connectorServer.start();
   }
  
   public void stopJMXConnector() throws Exception{
       connectorServer.stop();
   }
  
}

执行此RmiServer:
Server up and running


可以看到TCP 1099端口被listen
D:\Documents and Settings\mac>netstat -an

Active Connections

  Proto  Local Address          Foreign Address        State
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING
  TCP    0.0.0.0:445            0.0.0.0:0              LISTENING
  TCP    0.0.0.0:1025           0.0.0.0:0              LISTENING
  TCP    0.0.0.0:1099           0.0.0.0:0              LISTENING
  TCP    0.0.0.0:2737           0.0.0.0:0              LISTENING
  TCP    0.0.0.0:3306           0.0.0.0:0              LISTENING
  TCP    0.0.0.0:6059           0.0.0.0:0              LISTENING
  TCP    0.0.0.0:6648           0.0.0.0:0              LISTENING
2.通用客户端(典型的比如jconsole)
这里用的是jdk 1.6 jconsole
远程进程:
service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxconnector

用户名:
口令:
用户名和口令都可以空着,不必输入
点"连接",可以进入jconsole界面
可以在mbean下看到hello对象
3. 自定义client 端通过 RMI连接 Server
package com.machome.jmx.appTest;

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

import com.machome.bean.Hello;
import com.machome.bean.HelloMBean;



public class RmiClient
{
    private JMXServiceURL jmxUrl;
    private JMXConnector connector;
    private MBeanServerConnection mBeanServerconnection;
   
   
    public RmiClient()throws Exception{
        createJMXRmiConnector();
        createMBeanServerConnection();
    }
   
   public static void main(String[] args) throws Exception
   {
       new RmiClient().test();
      
   }
  
  
   private void createJMXRmiConnector() throws Exception{
          // 1.The JMXConnectorServer protocol
          String serverProtocol = "rmi";

          // 2.The RMI server's host
          // this is actually ignored by JSR 160
          String serverHost = "localhost";

          // 3.The host, port and path where the rmiregistry runs.
          String namingHost = "localhost";
          int namingPort = 1099;
          String jndiPath = "/jmxconnector";

          // 4. connector server url
          jmxUrl = new JMXServiceURL("service:jmx:" +
                  serverProtocol + "://" + serverHost +
                  "/jndi/rmi://" + namingHost + ":" +
                  namingPort + jndiPath);
         
          // 5. 生成 JMXConnector,连接到url一端
          // Connect a JSR 160 JMXConnector to the server side
          connector = JMXConnectorFactory.connect(jmxUrl);

   }
  
  
   private void createMBeanServerConnection() throws Exception{
       mBeanServerconnection = connector.getMBeanServerConnection();
   }
  
  
   public void test()throws Exception{
 
       ObjectName  oName = new ObjectName("mbean", "name", "hello");
       // 获取代理对象
       Object proxy = MBeanServerInvocationHandler
         .newProxyInstance(mBeanServerconnection,oName, HelloMBean.class, true);
                

       
        // 获取测试MBean,并执行它的(暴露出来被管理监控的)方法      
       HelloMBean helloMBean = (HelloMBean)proxy;
      
       helloMBean.setName("mac");
       helloMBean.printHello();
       helloMBean.printHello("haha");
   
   }
  
}
执行:
Server up and running
Hello, mac
Hello, haha


  • 上面例子中RMI中的rmiregistry MBean,我们采用的mc4j-tools.jar中的 NamingService,代码如下,代码并不复杂,其实我们也可以自定义一个代替它:
public class NamingService  implements NamingServiceMBean
{
    public NamingService()
    {
        this(1099);
    }

    public NamingService(int port)
    {
        setPort(port);
    }

    public void setPort(int port)
    {
        if(isRunning())
        {
            throw new IllegalStateException("NamingService is running, cannot change the port");
        } else
        {
            m_port = port;
            return;
        }
    }

    public int getPort()
    {
        return m_port;
    }

    public boolean isRunning()
    {
        return m_running;
    }

    public void start()
        throws RemoteException
    {
        if(!isRunning())
        {
            m_registry = LocateRegistry.createRegistry(getPort());
            m_running = true;
        }
    }

    public void stop()
        throws NoSuchObjectException
    {
        if(isRunning())
            m_running = !UnicastRemoteObject.unexportObject(m_registry, true);
    }

    public String[] list()
        throws RemoteException
    {
        if(!isRunning())
            throw new IllegalStateException("NamingService is not running");
        else
            return m_registry.list();
    }

    public void unbind(String name)
        throws RemoteException, NotBoundException
    {
        if(!isRunning())
        {
            throw new IllegalStateException("NamingService is not running");
        } else
        {
            m_registry.unbind(name);
            return;
        }
    }

    private int m_port;
    private Registry m_registry;
    private boolean m_running;
}
MBean接口:
public interface NamingServiceMBean
{

    public abstract void setPort(int i);

    public abstract int getPort();

    public abstract boolean isRunning();

    public abstract void start()
        throws RemoteException;

    public abstract void stop()
        throws NoSuchObjectException;

    public abstract String[] list()
        throws RemoteException;

    public abstract void unbind(String s)
        throws RemoteException, NotBoundException;
}


  • spring 下的 RMI connector
1.spring 配置如下:
   <!-- 1.定义自己的bean -->
    <bean id="hello" class="com.machome.bean.Hello" />  
   
    <!-- JMX configuration -->

    <!-- 2.创建一个mbeanServer bean-->
    <!--  如果你的spring是应用在容器中,则不需要此步骤,此步骤用于单独建立独立的MBeanServer中
    <bean id="mbeanServer"
          class="org.springframework.jmx.support.MBeanServerFactoryBean">
    </bean>   
    -->
       
    <!-- 3.定义assembler bean ,装配bean
        MetadataMBeanInfoAssembler是AutodetectCapableMBeanInfoAssembler 唯一实现
        spring文档中有专门介绍AutodetectCapableMBeanInfoAssembler的章节
     -->
    <bean id="assembler"
          class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
        <!-- 注入下面定义的jmxAttributeSource--> 
        <property name="attributeSource" ref="jmxAttributeSource"/>
    </bean>
       
    <!-- 4.定义 解释mbean中Annotation的bean-->
    <bean id="jmxAttributeSource"
          class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>


    <!-- 5.定义 RMI连接器,注意需要先定义一个naming 注册 bean-->
    <bean id="registry"     
         class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"
         destroy-method="destroy"> 

      <property name="port" value="1099" /> 
    </bean>
           
    <bean id="serverConnector"
       class="org.springframework.jmx.support.ConnectorServerFactoryBean"
       depends-on="registry"> 

        <property name="objectName" value="connector:name=rmi"/>
        <property name="serviceUrl"
        value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxconnector"/>
       
        <!--  如果上面步骤2.你单独定义一个mbeanServer,这里需要指定,否则rmi connector缺省是去寻找runtime环境内的MBeanserver(通常是容器的MBeanServer)
        <property name="server" ref="mbeanServer"/>
        -->
       
        <!-- 多线程启动connector,一个线程一个connector -->
        <property name="threaded" value="true" />
        <!-- 多线程启动connector,thread daemon --> 
        <property name="daemon" value="true" /> 
    </bean>
  
   
    <!-- 6.定义MBeanExporter bean, 这是spring jmx最核心的类 -->
    <bean id="mBeanExporter" class="org.springframework.jmx.export.MBeanExporter"
    lazy-init="false">
        <!-- 注入上面定义的assembler 两个bean注入-->
        <property name="assembler" ref="assembler"/>
       
        <!--  如果上面步骤2.你单独定义一个mbeanServer,这里需要指定,否则export bean缺省是去寻找runtime环境内的MBeanserver(通常是容器的MBeanServer)
        <property name="server" ref="mbeanServer"/>
        -->
       
        <!-- 将1.里面定义的mbean注册到mBeanExporter bean中 -->
        <property name="beans">
            <map>
                <entry key="mbean:name=hello" value-ref="hello"/>
            </map>
        </property>
       
    </bean>
2. web.xml装载spring 配置
    <!-- ##################### 初始化spring容器  ###########-->   
    <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>
               classpath:ssh-mysql.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
3.将项目部署入Tomcat
启动Tomcat
不需要执行任何servlet,action,只要spring随Tomcat启动,就会看到TCP 1099端口被listen
D:\Documents and Settings\mac>netstat -an

Active Connections

  Proto  Local Address          Foreign Address        State
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING
  TCP    0.0.0.0:445            0.0.0.0:0              LISTENING
  TCP    0.0.0.0:1025           0.0.0.0:0              LISTENING
  TCP    0.0.0.0:1099       0.0.0.0:0           LISTENING
  TCP    0.0.0.0:2737           0.0.0.0:0              LISTENING   
4. 执行通用客户端(典型的比如jconsole)
这里用的是jdk 1.6 jconsole
远程进程:
service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxconnector

用户名:
口令:
用户名和口令都可以空着,不必输入
 
点"连接",可以进入jconsole界面
与前面application RMI的例子不同,这里的不仅可以看到hello MBean对象,还能看到很多MBean对象,如Catalina目录下的很多MBean,因为spring没有定义新的MBeanServer,而是用 tomcat容器自己的MBeanServer,所以能看到很多Tomcat容器自己的service MBean
5.执行前面application RMI的例子的RmiClient
Hello, mac
Hello, haha
(我在spring rmi 例子和application rmi 例子 用的相同的serviceUrl ,都是
service:jmx:rmi://localhost/jndi/rmi: //localhost:1099/jmxconnector
因此客户端可以访问这两种例子的Server)


  • 简化后的spring RMI
其实spring 例子里的spring 配置的no.2,no.3.no.4都可以不用
MBeanServer可以不用建立,spring会自动搜索容器自己的MBeanServer,
MBeanInfoAssembler和AnnotationJmxAttributeSo urce 则只是提供注释MBean功能,以代替implement MBean接口,完全可以不用这种注释,而用传统的implement MBean接口

   <!-- 1.定义自己的bean -->
    <bean id="hello" class="com.machome.bean.Hello" />
   
    <!-- 5.定义 RMI连接器,注意需要先定义一个naming 注册 bean-->
    <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean" destroy-method="destroy"> 
      <property name="port" value="1099" /> 
    </bean>
           
    <bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry"> 
        <property name="objectName" value="connector:name=rmi"/>
        <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxconnector"/>

       
        <!-- 多线程启动connector,一个线程一个connector -->
        <property name="threaded" value="true" />
        <!-- 多线程启动connector,thread daemon --> 
        <property name="daemon" value="true" /> 
    </bean>
   
   
    <!-- 6.定义MBeanExporter bean, 这是spring jmx最核心的类 -->
    <bean id="mBeanExporter" class="org.springframework.jmx.export.MBeanExporter"
    lazy-init="false">
       
        <!-- 将1.里面定义的mbean注册到mBeanExporter bean中 -->
        <property name="beans">
            <map>
                <entry key="mbean:name=hello" value-ref="hello"/>
            </map>
        </property>
       
    </bean>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值