分布式通信框架-RMI讲解
什么是RPC
Remote procedure call protocal
RPC协议其实是一个规范。常用PRC框架:Dubbo、Thrif、RMI、Webservice、Hessain
网络协议和网络IO对于调用端和服务端来说是透明; 可以简单的理解为用户调用远程方法。不必在乎这个远程方法是怎么出现的,就跟调用本地方法一样。RPC框架做的事就是让我们像使用本地方法一样的去使用远程方法,但是其实RPC框架的底层有自己写好协议以便能远程通讯。
一个RPC框架包含的要素
RPC的框架自然少不了 RPC的服务器和要使用PRC框架的客户端。
自底向上分析:
1.一个通讯协议最基本的就是通讯,常见的通讯协议有TCP和UDP,这属于传输层 。
2.其次就是数据链路层,也可以认为是消息层(毕竟都是数据).
3.而RPC通讯框架在客户端连接时会返回给客户端一个代理对象来执行想要远程调用的方法。
服务器端则是通过RPC框架获取一个处理器。
4.再接着就是服务器端和客户端
这里只是先阐述一个概念,当然这个图并不完整,清接着往下看。
RMI的概述
RMI(remote method invocation) , 可以认为是RPC的java版本
RMI使用的是JRMP(Java Remote Messageing Protocol), JRMP是专门为java定制的通信协议,所以踏实纯java的分布式解决方案
如何实现一个RMI程序
1. 创建远程接口, 并且继承java.rmi.Remote接口
2. 实现远程接口,并且继承:UnicastRemoteObject
3. 创建服务器程序: createRegistry方法注册远程对象
4. 创建客户端程序
代码如下:
接口:模拟PRC的远程方法
import java.rmi.Remote; import java.rmi.RemoteException; /** * @Auther: Anthony * @Date: 2018/10/16 22:14 * @Description:接口 模拟PRC的远程方法 */ public interface ISayHello extends Remote{ public String sayHello(String name) throws RemoteException; }
具体实现:真正执行方法(继承UnicastRemoteObject表明是一个远程服务)
import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; /** * @Auther: Anthony * @Date: 2018/10/16 22:15 * @Description: */ public class SayHelloImpl extends UnicastRemoteObject implements ISayHello{ protected SayHelloImpl() throws RemoteException { } @Override public String sayHello(String name) throws RemoteException { return "hello->"+name; } }
创建server端
import java.net.MalformedURLException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; /** * @Auther: Anthony * @Date: 2018/10/16 22:19 * @Description: */ public class HelloServer { public static void main(String[] args) { try { ISayHello hello = new SayHelloImpl(); LocateRegistry.createRegistry(8888);//注册服务 Naming.bind("rmi://localhost:8888/sayHello",hello);//绑定服务 System.out.println("server start success"); } catch (RemoteException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (AlreadyBoundException e) { e.printStackTrace(); } } }
创建Client端
import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; /** * @Auther: Anthony * @Date: 2018/10/16 22:22 * @Description: */ public class HelloClient { public static void main(String[] args) { try { ISayHello hello = (ISayHello) Naming.lookup("rmi://localhost:8888/sayHello"); System.out.println(hello); System.out.println(hello.sayHello("wangqian")); } catch (NotBoundException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } }
这里我们需要做的就是Server端注册服务,Client端找到服务并调用方法
如果自己要去实现一个RMI
1. 编写服务器程序,暴露一个监听, 可以使用socket
2. 编写客户端程序,通过ip和端口连接到指定的服务器,并且将数据做封装(序列化)
3. 服务器端收到请求,先反序列化。再进行业务逻辑处理。把返回结果序列化返回
RMI给客户端返回的代理为(调用方法名+Stub),这里统一称为Stub。
服务器使用处理器是为Skeleton
具体RMI框架是怎么操作的呢?
1.server端在注册服务的时候,registry(可以理解为Dubbo里的注册中心 = =)就创建了一个代理对象Stub,
2.同时registry里也同时生成了skeleton对象
3.客户端访问远程服务,就返回创建好的Stub对象
4.Stub远程调用(这里其实就是底层通讯了),Skeleton(这里相当于是一个不断监听的socket)发现有请求过来就指定相应的服务,返回服务给Client。
这里就不贴代码了(知乎写博客是真的难受....)
给个地址:
https://github.com/MelonAnthony/BaseStudy/tree/master/qiansion-day1/src/main/java/per/wq/mvp/qiansion/demo
MelonAnthony/BaseStudy
源码分析
就拿上面的一段使用RMI框架的代码来说吧
ISayHello hello = new SayHelloImpl(); LocateRegistry.createRegistry(8888);//注册服务 Naming.bind("rmi://localhost:8888/sayHello",hello);//绑定服务 System.out.println("server start success");
先看这个createRegistry
if分支是判断安全性的,而我们传的端口是8888,所以走的是else分支
setup里的主要方法就是这个exportObject,可以认为是暴露服务。
先看第一个红框-创建了一个代理这里var4 就是RegistryImpl也就是一个Registry。由于var2是true,这里创建的就是一个Stub对象(不信的同学可以去这个createProxy里验证一下)。算了,我还是贴出来吧:逻辑或,这我就不解释了吧
然后是setSkeleton,详见下图:
同样是创建一个Skeleton。
这样我们上面说的时序图的1,2步就能说得通了。
至于3,4步,希望大家自己看一看源码,今天太晚了,我就不补充了。下次再来吧。
以上为本人自己的分析,有错误希望各路高人指点江山,指正一下。