avatar

java网络编程_自定义RPC框架

分布式架构网络通信

在分布式服务框架中,一个最基本的问题就是远程服务怎么通信的,在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());
    }
    }
    这样获取到的接口就可以直接调用接口,像调用本地函数一样使用了。

基于Netty实现RPC框架

文章作者: zenshin
文章链接: https://zlh.giserhub.com/2022/01/27/cl35o0n3y00h7p4tg00232jwv/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 zenshin's blog
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论