参考文献:
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 基本概念
- 管理资源(Manageable resource):实体对象
- 管理组件(MBean,managed bean):从资源的角度来看,它是一个对抽象的资源的一个描述,比如说如果资源是数据库,管理组件中可以提供数据库的一些描述信息,比如数据库服务器的运行地址、端口,类型以及最大连接数等等,但是这个类必须满足JMX规范中的提出的要求,比如命名规则和实现标准,类似于JavaBean。由于管理组件是资源的抽象,所以管理应用是直接面向MBean,也就说MBean会被暴露给管理应用来操作和访问,通过MBean中提供的属性和方法,MBean 一般都是Standard MBean
- 管理组件服务器(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中都要至少有一个属性;
- JMX代理(JMX Agent):MBean Server的容器,用于启动MBeanServer与配置JMX的其他组件,如Connector。该部分只是一个逻辑上的概念,该部分代码就相关与JMX Server。
- 协议适配器和连接器(Protocol adapters and connectors ):协议适配器和连接器是JMX Agent中的对象,将代理暴露给不同的管理应用和协议,这个和不同的数据库的驱动程序类似,每个数据库都有自己的一套协议来联系,为了保持进行连接,就需要在JDBC应用和数据库服务器之间通过不同的驱动程序关联。一个JMX Agent可以有任意数量的适配器和连接器;它们也是MBeans;
- 通知(Notification ):通知是由MBeans和MBean Server 提出的,其中封装了具体的事件和相应的数据。其他的MBeans或者Java对象可以注册作为监听器来接收这些通知,其实就是观察者设计模式在JMX中的应用;
3.2 JMX的架构
JMX的架构是组件式的,被设计为三层:
- 分布层(Distributed layer):包含可以使管理应用与JMX Agents交互的组件。一旦通过交互组件与JMX Agents建立连接,用户可以用管理工具来和注册在Agents中的MBeans进行交互;
- 代理层(Agent layer ):包含JMX Agent以及它们包含的MBean Servers。Agent layer的主要组件是MBean server,作为JMX Agents的核心,它充当MBeans的注册中心。该层提供了4个Agent 服务来使对MBean的管理更容易:计时器(Timer)、监控(monitoring)、动态加载MBean(dynamic MBean loading )、关系服务(relationship services );
- 指示层(Instrumentation layer ):包含代表可管理资源的MBeans。该层是最接近管理资源的,它由注册在Agents中的MBeans组成,这个MBean允许通过JMX Agent来管理。每个MBean都暴露出来针对底层资源的操作和访问;
具体的架构分层如下图:
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。
另外,还可看到每个启动的Java 进程还发布了一些系统的MBean,如Memeory相关的,可通过这个获取Java进行对内存的使用情况。
3.4 个人理解
JMX提供了一种对外发布MBean的方式,client可以通过一定的途径来获取这些MBean并使用这些MBean。获取这些MBean的方式也是在client端使用Connector。
Client使用MBean的方式可以通过Java RMI,或是其他的形式(这要看Agent层的Connector的协议)