什么是RPC
- RPC(Remote Procedure Call,远程过程调用)
- 一般用来实现部署在不同机器上的系统之间的方法调用,
- 使得程序能够像访问本地系统资源一样,通过网络传输去访问远端系统资源;(!!!)
- 对于客户端来说, 传输层使用什么协议,序列化、反序列化都是透明的
了解 Java RMI
- RMI 全称是remote method invocation – 远程方法调用,
- 一种用于远程过程调用的应用程序编程接口,是纯java 的网络分布式应用系统的核心解决方案之一。
- RMI 目前使用Java 远程消息交换协议JRMP(Java Remote Messageing Protocol) 进行通信,
- 由于JRMP 是专为Java对象制定的,是分布式应用系统的百分之百纯java 解决方案,
- 用Java RMI 开发的应用系统可以部署在任何支持JRE的平台上
Java RMI 代码实践
- 远程对象必须实现UnicastRemoteObject
- 这样才能保证客户端访问获得远程对象时,该远程对象会把自身的一个拷贝以Socket 形式传输给客户端
- 客户端获得的拷贝称为“stub” ,
- 而服务器端本身已经存在的远程对象成为“skeleton”,
- 此时客户端的stub 是客户端的一个代理,用于与服务器端进行通信
- 而skeleton 是服务端的一个代理
- 用于接收客户端的请求之后调用远程方法来响应客户端的请求
Java RMI 源码分析
- 远程对象发布
远程引用层
一步步解读源码
- 发布远程对象
- 看到上面的类图可以知道,这个地方会发布两个远程对象,一个是RegistryImpl、另外一个是我们自己写的RMI 实现类对象;
LocateRegistry.createRegistry(1099);
- 如果服务端指定的端口是1099 并且系统开启了安全管理器,那么就可以在限定的权限集内绕过系统的安全校验。
- 这里纯粹是为了提高效率, 真正的逻辑在this.setup(newUnicastServerRef())这个方法里面
- 有一个问题为什么断点进去的时候,会重复接收到多个请求
- 这是TCP协议特性
- TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。
- 然后接收端实体对已成功收到的包发回一个相应的确认(ACK);
- 如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
- TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
- 这个Target 对象基本上包含了全部的信息,等待TCP 调用。
- 包装实际对象,并将其曝露在TCP端口上,等待客户端调用
- 用skeleton、stub、UnicastServerRef 对象、id 和一个boolean 值构造了一个Target 对象
- LiveRef 与TCP 通信的类
- RegistryImpl extends RemoteServer implements Registry
- Skeleton
- private Hashtable<String, Remote> bindings = new Hashtable(101);
- 这个bindings 绑定服务(注册中心)
- Naming.rebind("rmi://127.0.0.1/Hello",helloService); //注册中心 key - value
- RegistryImpl_Stub
- stub
- LocateRegistry
- UnicastServerRef
- 内部引用了LiveRef
- UnicastRemoteObject
- 字面意思(单播远程对象)
- RemoteRef
- Remote
- TCPTransport
- 调用TCPTransport 的listen()方法,listen()方法创建了一个ServerSocket,并且启动了一条线程等待客户端的请求。
- Stub和Skeleton:
- 这两个的身份是一致的,都是作为代理的存在。
- 客户端的称作Stub,服务端的称作Skeleton。
- 要做到对程序员屏蔽远程方法调用的细节,这两个代理是必不可少的,包括网络连接等细节。
- Registry:顾名思义,可以认为Registry是一个“注册重心”,提供了服务名到服务的映射。
- 如果没有它,意味着客户端需要记住每个服务所在的端口号,这种设计显然是不优雅的。
- 看源码要看到什么程度:“看到你觉得你能说服自己就可以了”