RPC实践(一)起步从非RPC的RMI开始
来源:互联网 发布:sql查询学生总分 编辑:程序博客网 时间:2024/06/05 15:03
在工作中使用java编写服务的同学都有用过或者知道RMI,从RMI和RPC解决问题的出发点看,有一些是相同的,所以有相似之处,都提供了一种远程调用接口的能力,解决了分布式系统中相互之间调用的问题。不过他们之间还是有区别的。
一、RMI不是RPC
RMI是Java的一组开发分布式应用程序的API。RMI使用Java语言接口定义了远程对象,它集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。简单地说,这样使原先的程序在同一操作系统的方法调用,变成了不同操作系统之间程序的方法调用,由于J2EE是分布式程序平台,它以RMI机制实现程序组件在不同操作系统之间的通信。 但RMI的使用必须是在能够识别java代码的环境下使用,即必须有JVM的支持。因此,他只适合在java程序间的对象通信。如果不在 Java 环境下工作,或者需要与非 Java 环境通信,那么SOAP、RPC、CORAR等都是可以的。
RPC(Remote Method Invocation,远端过程调用) 与RMI的区别很明显,相比于RMI直接获取远端方法的签名,进行调用的方式,RPC使用的是C/S方式,发送请求到服务器,等待服务器返回结果。为了包装RPC的请求信息,推出了XML-RPC,客户端发送一条特定消息,该消息中必须包括名称、运行服务的程序以及输入参数。XML-RPC只能使用有限的数据类型种类和一些简单的数据结构。SOAP最主要的工作是使用标准的XML描述了RPC的请求信息(URI/类/方法/参数/返回值)。SOAP的方式,SOAP 是对如CORBA 和 RMI-IIOP 这样的重型 范例吸引人的替代。
RMI 与 RPC的相似性在于功能相似,我们可以从他们的功能图看他们的相似之处
从上面的功能框架看,RMI 和 RPC 实现的功能确实比较类似的, 当然RPC可以跨语言实现调用,功能上更为强大。如果从实现机制上看就会发现RMI 与 RPC 有很大不同。 RMI是基于类的远程调用方式,在两边有同一个类的主体和存根,而RPC 可以基于HTTP 或者别的通信协议,进行调用,在通信的数据包里,含有调用的方法,参数等序列化后的信息。
下面是RMI的机制原理图:
再来看一下RPC的机制原理:
RPC原理图与RMI原理图的最大区别在于,RMI是对象类的操作,客户端和服务端之间的通信协议已经由RMI自行解决。RPC调用,客户端与服务端之间传送的操作的数据内容,这些数据内容经过了序列化和反序列化的过程,这样的机制让RPC调用具有更为灵活的特性,因为只是通过通信(常用http,socket)协议来传送数据包,因此这种机制可以跨语言,跨平台,只要序列化和反序列化能够保证一致性,就可以实现灵活的调用。
二、RMI调用的简单例子
下面通过一个简单的例子来实践和体会RMI调用
2.1 远程对象接口和远程对象实现
对象实例:
package com.cwqsolo.rmidemo.enitity;import java.rmi.Remote;import java.rmi.RemoteException;/* * 首先定义一个继承Remote的接口 * 这个接口中定义的方法需要抛出RemoteException */public interface RmiEntityHello extends Remote { /** * 简单的方法,无需传入参数,只返回“Hello World!" */ public String helloWorld() throws RemoteException; /** * 带参数的方法:输入参数为名字,返回“名字,Hello World!” */ public String sayHello2Someone(String Name) throws RemoteException; }定义了一个继承remote的 接口,然后还需要定义个接口的实现类,并且这个实现类需要继承UnicastRemoteObject,否则客户端调用的都是本地而非服务端。这个接口的实现类如下:
package com.cwqsolo.rmidemo.enitity;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class RmiEntityHelloImpl extends UnicastRemoteObject implements RmiEntityHello { /** * 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须声明抛出RemoteException异常 */ public RmiEntityHelloImpl() throws RemoteException { } /** * 简单的方法,无需传入参数,只返回“Hello World!" */ public String helloWorld() throws RemoteException { return "Hello World!"; } /** * 带参数的方法:输入参数为名字,返回“名字,Hello World!” */ public String sayHello2Someone(String Name) throws RemoteException { return "Hello->" + Name + "!"; } }
2.2 服务端代码:
package com.cwqsolo.rmiserver;import java.net.MalformedURLException;import java.rmi.AlreadyBoundException;import java.rmi.Naming;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import com.cwqsolo.rmidemo.enitity.RmiEntityHello;import com.cwqsolo.rmidemo.enitity.RmiEntityHelloImpl;public class HelloServer { public static void main(String args[]) { try { //创建一个远程对象 ,注意对象是接口继承类 RmiEntityHello myhello = new RmiEntityHelloImpl(); //本地主机上的远程对象注册实例,并指定端口为5099,Java默认端口是1099) //缺少注册创建,则无法绑定对象到远程注册表上 LocateRegistry.createRegistry(5099); //把远程对象注册到RMI注册服务器上,并命名为Hello //绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略) Naming.bind("rmi://localhost:5099/Hello", myhello ); System.out.println(">>>>>INFO:远程RmiEntityHello对象绑定成功!"); } catch (RemoteException e) { System.out.println("创建远程对象发生异常!"); e.printStackTrace(); } catch (AlreadyBoundException e) { System.out.println("发生重复绑定对象异常!"); e.printStackTrace(); } catch (MalformedURLException e) { System.out.println("发生URL畸形异常!"); e.printStackTrace(); } } }
2.3 客户端代码
package com.cwqsolo.rmidemo.client;import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.NotBoundException;import java.rmi.RemoteException;import com.cwqsolo.rmidemo.enitity.RmiEntityHello;public class HelloClient { public static void main(String args[]){ try { //在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法 RmiEntityHello myhello =(RmiEntityHello) Naming.lookup("rmi://localhost:5099/Hello"); System.out.println(myhello.helloWorld()); System.out.println(myhello.sayHello2Someone( "Honey") ); } catch (NotBoundException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } }
三:例子运行情况
首先启动服务端,服务端启动后,对端口进行监听,等待客户端连接
然后客户端连接并查询远程对象
最后调用远程对象的方法
服务端日志:
>>>>>INFO:远程RmiEntityHello对象绑定成功!
客户端日志:
Hello World!
Hello->Honey!
四:java rmi机制的一些实现
(这里的内容参考网络)
4.1:UnicastRemoteObject.java
基类的构造方法将远程对象发布到一个随机端口上,构造函数
protected UnicastRemoteObject(int port) throws RemoteException { this.port = port; exportObject((Remote) this, port); } public static Remote exportObject(Remote obj, int port) throws RemoteException { return exportObject(obj, new UnicastServerRef(port)); } private static Remote exportObject(Remote obj, UnicastServerRef sref) throws RemoteException { // if obj extends UnicastRemoteObject, set its ref. if (obj instanceof UnicastRemoteObject) { ((UnicastRemoteObject) obj).ref = sref; } return sref.exportObject(obj, null, false); }
4.2 UnicastServerRef.java
public Remote exportObject(Remote impl, Object data, boolean permanent) throws RemoteException { Class implClass = impl.getClass(); Remote stub; try { stub = Util.createProxy(implClass, getClientRef(), forceStubUse); } catch (IllegalArgumentException e) { throw new ExportException( "remote object implements illegal remote interface", e); } if (stub instanceof RemoteStub) { setSkeleton(impl); } Target target = new Target(impl, this, stub, ref.getObjID(), permanent); ref.exportObject(target); hashToMethod_Map = hashToMethod_Maps.get(implClass); return stub; }这里的stub变量就是我们lookup出来的远程对象的存根对象。而target保留了远程对象的信息集合:对象,存根,objId等。
4.3 TCPTransport.java
public void exportObject(Target target) throws RemoteException { /* * Ensure that a server socket is listening, and count this * export while synchronized to prevent the server socket from * being closed due to concurrent unexports. */ synchronized (this) { listen(); exportCount++; } /* * Try to add the Target to the exported object table; keep * counting this export (to keep server socket open) only if * that succeeds. */ boolean ok = false; try { super.exportObject(target); ok = true; } finally { if (!ok) { synchronized (this) { decrementExportCount(); } } } }listen()启动监听。这里对TcpTransport加了同步,防止多个线程同时执行,同时也防止同一端口启动多次。一次TcpTransport会有一个监听端口,而一个端口上可能会发部多个远程对象。exportCount计录该TcpTransport发布了多少个对象。
public void exportObject(Target target) throws RemoteException { target.setExportedTransport(this); ObjectTable.putTarget(target); }ObjectTable为静态方法调用,那么所有的远程对象信息(target)都会放到这个对象表中。我们这个target包含了远程对象几乎所有的信息,找到他,就找到远程对象的存根对象。
4.4 bind (RegistryImpl.java )
public void bind(String name, Remote obj) throws RemoteException, AlreadyBoundException, AccessException { checkAccess("Registry.bind"); synchronized (bindings) { Remote curr = bindings.get(name); if (curr != null) throw new AlreadyBoundException(name); bindings.put(name, obj); } }
4.5 RegistryImpl_Skel.class
case 2: // lookup(String) { java.lang.String $param_String_1; try { java.io.ObjectInput in = call.getInputStream(); $param_String_1 = (java.lang.String) in.readObject(); } catch (java.io.IOException e) { throw new java.rmi.UnmarshalException("error unmarshalling arguments", e); } catch (java.lang.ClassNotFoundException e) { throw new java.rmi.UnmarshalException("error unmarshalling arguments", e); } finally { call.releaseInputStream(); } java.rmi.Remote $result = server.lookup($param_String_1); try { java.io.ObjectOutput out = call.getResultStream(true); out.writeObject($result); } catch (java.io.IOException e) { throw new java.rmi.MarshalException("error marshalling return", e); } break; }
4.6 MarshalOutputStream.java
protected final Object replaceObject(Object obj) throws IOException { if ((obj instanceof Remote) && !(obj instanceof RemoteStub)) { Target target = ObjectTable.getTarget((Remote) obj); if (target != null) { return target.getStub(); } } return obj; }
附:执行10000次2.174 seconds ,每秒4600次。
- RPC实践(一)起步从非RPC的RMI开始
- 从RPC开始(一)
- 理解Android系统的进程间通信原理(一)----RPC中的-代理模式 -RMI-RPC
- RPC-RMI
- 从RPC开始(二)、序列化
- RPC与RMI的区别
- RMI RPC JMS 的区别
- RMI与RPC的区别
- RMI与RPC的区别
- RPC/RMI/SOAP的区别
- RMI与RPC的区别
- RMI与RPC的区别
- RMI与RPC的区别
- RPC与RMI的区别
- RPC与RMI的区别
- RMI与RPC的区别
- xml-rpc的实践
- RPC/RMI之一:RPC简介
- 关于Linux系统内存:cached过大问题,解决方案。(运维)
- 清理
- 【android 文件的基本操作】
- 如何解决在服务中处理activity中的事件
- UIGestureRecognizer【手势】属性、代理方法以及子类属性、方法
- RPC实践(一)起步从非RPC的RMI开始
- 简单的使用doGet, doPost方法
- JAVA基本数据类型范围和float、double精度问题
- 使用eclipse前的一些设置(环境配置好后)
- Learning Task Grouping and Overlap in Multi-Task Learning
- Retrofit的使用
- 12312313
- win10 vs2015 libxml2编译 各种版本
- 软件测试学习笔记(三)——软件测试过程