分布式架构网络通信
在分布式服务框架中,一个最基本的问题就是远程服务怎么通信的,在java领域中有很多可实现远程通信的技术,例如:RMI、Hessian、SOAP、ESB和JMS等
基本原理
要实现网络机器间的通信,首先得来看看计算机系统网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现,其中传输协议比较出名的有tcp、udp等等,tcp、udp都是基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO,主要有BIO、NIO、AIO三种方式,所有的分布式应用通讯都基于这个原理而实现。
什么是RPC
RPC全称为remote procedure call,即远程过程调用。借助RPC可以做到像本地调用一样调用远程服务,是一种进程间的通信方式
比如两台服务器A和B,A服务器上部署了一个应用,B服务器上部署了一个应用,A服务器上的应用想调用B服务器上的应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用的过程
RPC架构
一个完整的RPC架构里面包含了四个核心的组件,分别是Client、Client Stub、Server以及Server Stub,这个Stub可以认为是存根。
- 客户端(Client),服务的调用方
- 客户端存根(Client Stub)存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方
- 服务端(Server):真正的服务提供者
- 服务端存根(Server Stub),接收客户端发送过来的消息,将消息解包,并调用本地的方法。

RPC架构调用过程:
- 客户端以本地调用方式(即以接口的方式)调用服务
- 客户端村给你接收到调用后,参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制)
- 客户端通过Socket将消息发送到服务器
- 服务器存根收到消息后进行解码(将消息反序列化)
- 服务端存根根据解码结果调用本地的服务
- 服务处理
- 本地服务执行并返回结果给服务端存根
- 服务端存根将返回结果打包成消息(将结果消息对象序列化)
- 服务端通过socket将消息发送到客户端
- 客户端存根接收到结果消息,并进行解码(将结果消息发序列化)
- 客户端得到最终结果
RPC的目标是要把2,3,4,5,7,8,9,10这些步骤都封装起来,只剩下1,6,11
在java中RPC框架比较多 ,常见的有Hessian、gRPC、Dubbo等,其实对RPC框架而言,核心模块就是通讯和序列化
RMI
java RMI即远程方法调用(Remote Method Invocation),一种用于实现远程过程调用(RPC-Remote proceduce call)的java API,能直接传输序列化后的java对象,它的实现依赖于java虚拟机,因此它仅支持从一个jvm到另一个jvm的调用
代码实现案例
首先我们所有需要远程调用的接口都需要继承Remote
,然后所有的业务实现类都需要继承UnicastRemoteObject
- 服务端代码
public class RMIServer {
public static void main(String[] args) throws RemoteException {
// 1、 注册regstry实例 绑定端口号
Registry registry = LocateRegistry.createRegistry(9998);
// 2、创建远程对象
IUserService userService = new UserService();
// 3、将远程对象注册到RMI服务器上即(服务端注册表上)
registry.rebind("userService",userService);
System.out.println("RMI服务端启动成功");
}
} - 客户端代码这样获取到的接口就可以直接调用接口,像调用本地函数一样使用了。
public class RMIClient {
public static void main(String[] args) throws RemoteException, NotBoundException {
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9998);
// 通过registry实例查找远程对象
IUserService userService = (IUserService)registry.lookup("userService");
User user = userService.getByUserId(1);
System.out.println(user.getId()+"--------"+user.getName());
}
}