RMI的原理
RMI执行过程图如下:
1.1、Remote Service和注册表(Locate Registry)还有Remote Client都可以是不同的jvm。然后注册的时候都是把服务注册到Naming里面去,每一个Naming都会有有一个Remote Stub就是服务端实现接口生成的代理类对象。
Locate Registry:类似于注册中心,就是一个公共的区间。绑定的东西是绑定到注册中心上的,调用的时候也是从Locate Registry里拿的。
1.2、然后会反向生成Remote Skeleton骨架,然后通过远程的引用层再到传送层,通过Socket进行通信。
1.3、Remote Service和Remote Client是通过远程机器协议(JRMP)进行通信的。
1.4、Remote Client去Naming去查找,通过RMI的协议看能否找到对应的代理对象,如果有的话,就会把这个代理对象给返回出来。
1.5、再到Remote Client的远程引用层再到Remote Client的传送层 ,从而和Remote Service实现透明化的通信。
2、远程方法调用过程:
①:客户端通过接口来发送一个请求。
②:接口又通过java中的jvm,来生成 一个stub的对象,相当于是一个根对象,就是一个代理。
③:代理将所有的网络通信通过③拿到④这边能够解析的编码格式,Skeleton也是服务端发布服务的代理类,这个代理类的作用是真正去调用服务端的接口的实现类中对应的方法。
上面用了两个代理,一个是代理客户端的,一个是代理服务端的,不管哪种代理,都是用socket来实现远程通信的。
④:它会把Skeleton骨架,刚 从客户端发过来的来进行解码,解码为服务端所调用方法所用的参数及其对应的类型。
⑤:然后去服务端去调用。
⑥:把返回的结果再返回给Skeleton。
⑦:Skeleton按照他们规定好的方式来包装数据。
⑧:然后发送到客户端的代理对象中。
⑨:客户端把数据解码。
⑩:返回到客户端。
2.1、区分下TCP和RPC还有RMI:
TCP:传输控制层协议。rpc:远程过程调用的框架。RMI:远程调用接口。
3、源码分析RMI如何去做通信的?
3.1、服务端:有一个createRegistry的方法
在这个方法里面有一个接口对应的实现类RegistryImpl,然后把这个port放入到了构造函数里面去
public static Registry createRegistry(int port) throws RemoteException {
return new RegistryImpl(port);
}
public RegistryImpl(final int var1) throws RemoteException {
this.bindings = new Hashtable(101);
//如果这个接口是1099并且是系统安全的
if (var1 == 1099 && System.getSecurityManager() != null) {
try {
//进行权限的验证
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Void run() throws RemoteException {
LiveRef var1x = new LiveRef(RegistryImpl.id, var1);
RegistryImpl.this.setup(new UnicastServerRef(var1x, (var0) -> {
return RegistryImpl.registryFilter(var0);
}));
return null;
}
}, (AccessControlContext)null, new SocketPermission("localhost:" + var1, "listen,accept"));
} catch (PrivilegedActionException var3) {
throw (RemoteException)var3.getException();
}
} else {
//如果不在1099端口的话这里创建一个LiveRef的引用类,这个引用类是可以支持克隆的。
//是可以支持对象拷贝的,一般是做浅拷贝的,不是深拷贝。
LiveRef var2 = new LiveRef(id, var1);
this.setup(new UnicastServerRef(var2, RegistryImpl::registryFilter));
}
}
在exportObject这个方法中
public Remote exportObject(Remote var1, Object var2, boolean var3) throws RemoteException {
Class var4 = var1.getClass();
Remote var5;
try {
//通过客户端引用的接口生成一个代理对象var5
//由于自己写的接口的代码是继承了Remote的
//这也就是在服务端去调用不能改变路径的原因
var5 = Util.createProxy(var4, this.getClientRef(), this.forceStubUse);
} catch (IllegalArgumentException var7) {
throw new ExportException("remote object implements illegal remote interface", var7);
}
- //判断生成的代理对象是否是一个根对象
if (var5 instanceof RemoteStub) {
//是这个对象的话直接设置到skeleton里面
this.setSkeleton(var1);
}
- //构建一个新的目标对象
Target var6 = new Target(var1, this, var5, this.ref.getObjID(), var3);
- //新的对象又会去执行exportObject的操作
this.ref.exportObject(var6);
this.hashToMethod_Map = (Map)hashToMethod_Maps.get(var4);
return var5;
}
在Naming.bind方法中:这个方法是对端口的绑定,得到Registry注册的列表 ,然后再把其放入到注册中心里面去。
public static void bind(String name, Remote obj)
throws AlreadyBoundException,
java.net.MalformedURLException,
RemoteException
{
ParsedNamingURL parsed = parseURL(name);
Registry registry = getRegistry(parsed);
if (obj == null)
throw new NullPointerException("cannot bind to null");
registry.bind(parsed.name, obj);
}
RMI的缺点:①、不是跨语言的。②、灵活性太低了。
在spring很老的版本中就封装了RMI。
未完待续!!如果哪里写的有问题,请指出。xxxxxxxxxxxxxxxxxxxx,谢谢。