之前看过一个RMI的简单示例。这篇文章简单的分析一下RMI的原理。
RMI应用程序的体系结构
上一个例子中,我们编写了两个程序,一个服务端(Server)和一个客户端(Client)。
- 在Server内创建一个远程对象(CalcServiceImpl),并生成存根,注册到Registry中。
- 客户端请求远程对象,并调用对象的方法。
- Transport Layer:RMI传输层实现网络连接与通信的数据传递。
- Stub:存根是远程对象在客户端的代理,它驻留在客户端系统中,充当客户端程序的网关。
- Skeleton:骨架驻留在服务端系统中,Stub通过Skeleton传递参数给远程服务对象。
- RRL(Remote Reference Layer):RRL是负责管理客户端到远程对象的引用。
RMI的工作原理
- 当客户端调用远程对象时,它会被Stub接收,并把请求传递给RRL
- 当客户端RRL接收到请求后,它会调用RemoteRef的invoke()方法。最终这个请求会被传递到服务端的RRL中。
- 服务端RRL将请求发给Skeleton(服务端的一个代理类),最终调用服务端中的服务对象方法。
- 服务方法返回的结果按原路返回给客户端。
Marshalling与Unmarshalling
当客户端调用远程对象的方法时,可能需要传递一些参数。这些参数将会被包装在消息中通过网络发送给服务端。这些参数会被序列化成二进制数据流传输到服务端,这个过程也被叫做Marshalling;相反服务端需要把二进制数据流反序列化还原,这个过程也被叫做Unmarshalling。
事实上这两个过程使用的使我们非常熟悉的ObjectOutputStream和ObjectInputStream。只不过,RMI进一步包装了这两个类:
这两个过程之间涉及到的消息通信协议,也被称为JRMP协议,Java Remote Message Protocol
RmiRegistry的作用
RmiRegistry用于管理所有的远程服务。
服务器端创建对象时,都会使用Registry.bind
或Registry.rebind
方法向RmiRegistry注册对象。每个服务对象都对应着一个唯一的服务名。
客户端调用远程对象时,客户端需要远程对象的引用。客户端可以根据服务名调用Registry.lookup
方法从RmiRegistry中获取对象。
分析Stub中的源代码
在上面的体系架构中,提到了Stub与Skeleton,但是在程序中我们并没有看到这两个类,那是因为从Java 5.0之后,Java使用java.lang.reflect.Proxy
为我们动态生成了这两个类。
service.getClass()
打印得到的是:class com.sun.proxy.$Proxy0
在Java 5.0之前需要我们使用rmic
命令根据服务实现类编译生成存根。
1 2
| # rmic -keep cn.hff.service.CalcServiceImpl
|
生成源码的Stub对象如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
|
package cn.hff.service;
public final class CalcServiceImpl_Stub extends java.rmi.server.RemoteStub implements cn.hff.service.ICalcService { private static final long serialVersionUID = 2;
private static java.lang.reflect.Method $method_add_0; private static java.lang.reflect.Method $method_minus_1;
static { try { $method_add_0 = cn.hff.service.ICalcService.class.getMethod("add", new java.lang.Class[] { int.class, int.class }); $method_minus_1 = cn.hff.service.ICalcService.class.getMethod("minus", new java.lang.Class[] { int.class, int.class }); } catch (java.lang.NoSuchMethodException e) { throw new java.lang.NoSuchMethodError("stub class initialization failed"); } }
public CalcServiceImpl_Stub(java.rmi.server.RemoteRef ref) { super(ref); }
public int add(int $param_int_1, int $param_int_2) throws java.rmi.RemoteException { try {
Object $result = ref.invoke(this, $method_add_0, new java.lang.Object[] { new java.lang.Integer($param_int_1), new java.lang.Integer($param_int_2) }, -7734458262622125146L); return ((java.lang.Integer) $result).intValue(); } catch (java.lang.RuntimeException e) { throw e; } catch (java.rmi.RemoteException e) { throw e; } catch (java.lang.Exception e) { throw new java.rmi.UnexpectedException("undeclared checked exception", e); } }
public int minus(int $param_int_1, int $param_int_2) throws java.rmi.RemoteException { try { Object $result = ref.invoke(this, $method_minus_1, new java.lang.Object[] { new java.lang.Integer($param_int_1), new java.lang.Integer($param_int_2) }, 6403391039451205508L); return ((java.lang.Integer) $result).intValue(); } catch (java.lang.RuntimeException e) { throw e; } catch (java.rmi.RemoteException e) { throw e; } catch (java.lang.Exception e) { throw new java.rmi.UnexpectedException("undeclared checked exception", e); } } }
|
RemoteRef的实现类UnicaseRef的源码可以点击这里查看。
本作品采用 知识共享署名 4.0 国际许可协议 进行许可。
转载时请注明原文链接:https://blog.hufeifei.cn/2017/12/Java/RMI%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/