天天看点

JMX,RMI与RPC1. RPC2. RMI3.JMX

参考文献:

https

文章目录

  • 1. RPC
    • 1.1 RMI
    • 1.2 hessian://
    • 1.3 http://
    • 1.4 thrift://
    • 1.5 rest://
  • 2. RMI
    • 2.1 示例
      • 2.1.1 Server端
      • 2.1.2 client端:
  • 3.JMX
    • 3.1 基本概念
    • 3.2 JMX的架构
    • 3.3 示例
      • 3.3.1 MBean
      • 3.3.2 Agent 与MBeanServer
      • 3.3.3 client
      • 3.3.4 其他说明
    • 3.4 个人理解

个人对于这几个的概念有点模糊,故在此记录一下,省的又忘。

1. RPC

概念:它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

为了实现RPC协议,一般的RPC框架采用的是一种分层结构,包括:传输层,序列化层,动态代理层,他们各自负责 RPC 调用生命周期中的一环。简单描述下各个层之间的关系:

  • protocol 层主要用于配置 refer(发现服务) 和 exporter(暴露服务) 的实现方式,transport 层定义了传输的方式,codec 层诠释了具体传输过程中报文解析的方式,serialize 层负责将对象转换成字节,以用于传输,proxy 层负责将这些细节屏蔽。
  • 它们的包含关系如下:protocol > transport > codec > serialize

实现RPC的方式有很多,下面简要说明

1.1 RMI

RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式。

适用场景:常规远程服务方法调用,与原生RMI服务互操作

1.2 hessian://

Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。

Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:

  • 提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用
  • 或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用。

Hessian 在之前介绍过,当时仅仅是用它来作为序列化工具,但其本身其实就是一个协议,可以用来做远程通信。

适用场景:页面传输,文件传输,或与原生hessian服务互操作

1.3 http://

基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现

适用场景:需同时给应用程序和浏览器 JS 使用的服务。

1.4 thrift://

当前 dubbo 支持的 thrift 协议是对 thrift 原生协议的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。

1.5 rest://

JAX-RS 是标准的 Java REST API,得到了业界的广泛支持和应用,其著名的开源实现就有很多,包括 Oracle 的 Jersey,RedHat 的 RestEasy,Apache 的 CXF 和 Wink,以及 restlet 等等。另外,所有支持 JavaEE 6.0 以上规范的商用 JavaEE 应用服务器都对 JAX-RS 提供了支持。因此,JAX-RS 是一种已经非常成熟的解决方案,并且采用它没有任何所谓 vendor lock-in 的问题。

更多的资料请自行 google 或者百度一下。就学习 JAX-RS 来说,一般主要掌握其各种 annotation 的用法即可。

2. RMI

概念:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。
RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信。JRMP是专为Java的远程对象制定的协议,由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信(意思是只支持客户端和服务器端都是Java程序的代码的远程调用)。
           

2.1 示例

2.1.1 Server端

package cn.com.tt.rmiserver.stub;

import java.rmi.Remote;
import java.rmi.RemoteException;

import cn.com.tt.rmiserver.bean.Account;

public interface UserManagerInterface extends Remote{
    public String getUserName() throws RemoteException;
    public Account getAdminAccount() throws RemoteException;
}
           
package cn.com.tt.rmiserver;

import java.rmi.RemoteException;

import cn.com.tt.rmiserver.stub.UserManagerInterface;
import cn.com.tt.rmiserver.bean.Account;

public class UserManagerImp implements UserManagerInterface {
    public UserManagerImp() throws RemoteException {

    }
    private static final long serialVersionUID = -3111492742628447261L;

    public String getUserName() throws RemoteException{
        return "TT";
    }
    public Account getAdminAccount() throws RemoteException{
        Account account=new Account();
        account.setUsername("TT");
        account.setPassword("123456");
        return account;
    }
}
           
package cn.com.tt.rmiserver.bean;

import java.io.Serializable;

public class Account implements Serializable,Cloneable{
    private static final long serialVersionUID = -1858518369668584532L;
    private String username;
    private String password;
    
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
           
package cn.com.tt.rmiserver.entry;

import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import cn.com.tt.rmiserver.UserManagerImp;
import cn.com.tt.rmiserver.stub.UserManagerInterface;

public class Entry {
    public static void main(String []args) throws AlreadyBoundException, RemoteException{
        UserManagerImp userManager=new UserManagerImp();
        UserManagerInterface userManagerI=(UserManagerInterface)UnicastRemoteObject.exportObject(userManager,0);
        // Bind the remote object's stub in the registry
        Registry registry = LocateRegistry.createRegistry(2002);
       
        registry.rebind("userManager", userManagerI);
        System.out.println("server is ready");
        }
}
           

2.1.2 client端:

package weiblog.rmi;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

import cn.com.tt.rmiserver.stub.UserManagerInterface;

public class ClientEntry {
    
    public static void main(String []args){
        
        try {
            Registry registry = LocateRegistry.getRegistry("localhost",2004);
            UserManagerInterface userManager = (UserManagerInterface)registry.lookup("userManager");
            System.out.println("用户名是"+userManager.getAdminAccount().getUsername()
                    +"密码"+userManager.getAdminAccount().getPassword());
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NotBoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }

}
           

3.JMX

3.1 基本概念

  1. 管理资源(Manageable resource):实体对象
  2. 管理组件(MBean,managed bean):从资源的角度来看,它是一个对抽象的资源的一个描述,比如说如果资源是数据库,管理组件中可以提供数据库的一些描述信息,比如数据库服务器的运行地址、端口,类型以及最大连接数等等,但是这个类必须满足JMX规范中的提出的要求,比如命名规则和实现标准,类似于JavaBean。由于管理组件是资源的抽象,所以管理应用是直接面向MBean,也就说MBean会被暴露给管理应用来操作和访问,通过MBean中提供的属性和方法,MBean 一般都是Standard MBean
  3. 管理组件服务器(MBean Server):简单的来看,它是一个容器,用来盛装和管理一组MBeans,它是整个JMX管理环境的核心。由于其中有很多的MBean,所以它必须提供一种机制来区分各个MBean,这就是注册机制,每个添加到MBean Server的MBean在注册的时候都要提供一个ObjectName来区分彼此,MBean Server 通过这个ObjectName来查找每个MBean,在JMX中是通过ObjectName类来为每个MBean提供唯一的一个标识,它包括两部分:
    • 域名:这个域名通常是和想要注册到的MBean Server的名称标识相同,以便根据功能模块区分不同MBean Server中的MBean;
    • 键值对列表:被用来唯一的标识MBean,也提供了关于该MBean的信息,形式如下:HelloAgent:name=helloWorld;其中的属性不一定是真实的MBean的属性,仅仅要求当和其他的MBean比较的时候能够唯一标识,每个ObjectName中都要至少有一个属性;
  1. JMX代理(JMX Agent):MBean Server的容器,用于启动MBeanServer与配置JMX的其他组件,如Connector。该部分只是一个逻辑上的概念,该部分代码就相关与JMX Server。
  2. 协议适配器和连接器(Protocol adapters and connectors ):协议适配器和连接器是JMX Agent中的对象,将代理暴露给不同的管理应用和协议,这个和不同的数据库的驱动程序类似,每个数据库都有自己的一套协议来联系,为了保持进行连接,就需要在JDBC应用和数据库服务器之间通过不同的驱动程序关联。一个JMX Agent可以有任意数量的适配器和连接器;它们也是MBeans;
  3. 通知(Notification ):通知是由MBeans和MBean Server 提出的,其中封装了具体的事件和相应的数据。其他的MBeans或者Java对象可以注册作为监听器来接收这些通知,其实就是观察者设计模式在JMX中的应用;

3.2 JMX的架构

JMX的架构是组件式的,被设计为三层:

  1. 分布层(Distributed layer):包含可以使管理应用与JMX Agents交互的组件。一旦通过交互组件与JMX Agents建立连接,用户可以用管理工具来和注册在Agents中的MBeans进行交互;
  2. 代理层(Agent layer ):包含JMX Agent以及它们包含的MBean Servers。Agent layer的主要组件是MBean server,作为JMX Agents的核心,它充当MBeans的注册中心。该层提供了4个Agent 服务来使对MBean的管理更容易:计时器(Timer)、监控(monitoring)、动态加载MBean(dynamic MBean loading )、关系服务(relationship services );
  3. 指示层(Instrumentation layer ):包含代表可管理资源的MBeans。该层是最接近管理资源的,它由注册在Agents中的MBeans组成,这个MBean允许通过JMX Agent来管理。每个MBean都暴露出来针对底层资源的操作和访问;

具体的架构分层如下图:

JMX,RMI与RPC1. RPC2. RMI3.JMX

3.3 示例

3.3.1 MBean

public interface HelloMBean {
    public String getName();
    public void setName(String name);
    public String printHello();
    public String printHello(String whoName);
}

class Hello implements HelloMBean {
    private String name;
     
    @Override
    public String getName() {
        return name;
    }
  
    @Override
    public void setName(String name) {
        this.name = name;
    }
  
    @Override
    public String printHello() {
        return "Hello "+ name;
    }
  
    @Override
    public String printHello(String whoName) {
        return "Hello  " + whoName;
    }
}
           

3.3.2 Agent 与MBeanServer

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 HelloAgent {
    public static void main(String[] args) throws Exception {
        //create mbean server
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
         
        //create object name
        ObjectName objectName = new ObjectName("jmxBean:name=hello");
         
        //create mbean and register mbean 
        server.registerMBean(new Hello(), objectName);
         
         
        /**
         * JMXConnectorServer service 
         */
        //这句话非常重要,不能缺少!注册一个端口,绑定url后,客户端就可以使用rmi通过url方式来连接JMXConnectorServer 
        Registry registry = LocateRegistry.createRegistry(1099);
         
        //构造JMXServiceURL
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
        //创建JMXConnectorServer
        JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, server);  
        //启动
        cs.start();
    }
}
           

3.3.3 client

import java.util.Iterator;
import java.util.Set;
import javax.management.Attribute;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
 
public class JMXClient {
    public static void main(String[] args) throws Exception {  
        //connect JMX  
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");  
        JMXConnector jmxc = JMXConnectorFactory.connect(url,null);  
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();       
        ObjectName mbeanName = new ObjectName("jmxBean:name=hello");     
           
        //print domains  
        System.out.println("Domains:---------------");  
        String domains[] = mbsc.getDomains();  
        for (int i = 0; i < domains.length; i++) {         
            System.out.println("Domain[" + i +"] = " + domains[i]);      
        }     
        System.out.println();
         
        //MBean count  
        System.out.println("MBean count:---------------");  
        System.out.println("MBean count = " + mbsc.getMBeanCount()); 
        System.out.println();
           
        //process attribute  
        System.out.println("process attribute:---------------"); 
        mbsc.setAttribute(mbeanName, new Attribute("Name", "newName")); //set value  
        System.out.println("Name = " + mbsc.getAttribute(mbeanName, "Name")); //get value  
        System.out.println();
           
        //invoke via proxy  
        System.out.println("invoke via proxy:---------------"); 
        HelloMBean proxy = (HelloMBean) MBeanServerInvocationHandler.newProxyInstance(mbsc, mbeanName, HelloMBean.class, false);          
        System.out.println(proxy.printHello());  
        System.out.println(proxy.printHello("zhangsan")); 
        System.out.println();
           
        //invoke via rmi  
        System.out.println("invoke via rmi:---------------"); 
        System.out.println(mbsc.invoke(mbeanName, "printHello", null, null));           
        System.out.println(mbsc.invoke(mbeanName, "printHello", new Object[] { "lisi" }, new String[] { String.class.getName() }));     
        System.out.println();
           
        //get mbean information  
        System.out.println("get mbean information:---------------"); 
        MBeanInfo info = mbsc.getMBeanInfo(mbeanName);           
        System.out.println("Hello Class:" + info.getClassName());        
        System.out.println("Hello Attribute:" + info.getAttributes()[0].getName());       
        System.out.println("Hello Operation:" + info.getOperations()[0].getName());     
        System.out.println();
           
        //ObjectName of MBean  
        System.out.println("ObjectName of MBean:---------------");         
        Set set = mbsc.queryMBeans(null, null);  
        for (Iterator it = set.iterator(); it.hasNext();) {  
            ObjectInstance oi = (ObjectInstance)it.next();          
            System.out.println(oi.getObjectName());          
        }  
         
        jmxc.close();       
    }    
}
           

3.3.4 其他说明

在Agent层启动起来后,其实不用Client也可以测试,使用Java自带的工具JConsole就可以查看Agent上的MBean。

JMX,RMI与RPC1. RPC2. RMI3.JMX

另外,还可看到每个启动的Java 进程还发布了一些系统的MBean,如Memeory相关的,可通过这个获取Java进行对内存的使用情况。

3.4 个人理解

JMX提供了一种对外发布MBean的方式,client可以通过一定的途径来获取这些MBean并使用这些MBean。获取这些MBean的方式也是在client端使用Connector。

Client使用MBean的方式可以通过Java RMI,或是其他的形式(这要看Agent层的Connector的协议)