RPC功能的简单Java方式实现以及思考

每篇一句:

We laughed and kept saying ‘see u soon’, but inside we both knew we’d never see each other again.


RPC简介:

RPC(Remote Procedure Call)—-远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如 TCPUDP ,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

RPC采用 客户机/服务器 模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。


Java实现:

  • Server提供:

    • HelloWorldService.java

      public interface HelloWorldService {
          public String hello(String name);
      }
      
    • HelloWorldServiceImpl.java

      public class HelloWorldServiceImpl implements HelloWorldService {
          @Override
          public String hello(String name) {
              return "Hello! " + name;
          }
      }
      
    • RPC_Server.java
      import java.io.IOException;
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.lang.reflect.Method;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.util.Arrays;
      
      public class RPC_Server {
          public static void main(String[] args){
              ServerSocket ss = null;
              Socket socket = null;
              ObjectOutputStream out  = null;
              try {
                  ss = new ServerSocket(8090);
                  while(true){
                      System.out.println("----Server Start!-----");
                      socket = ss.accept();
      
                      // 从socket获取信息
                      ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                      // 获取消息的顺序要与客户端添加的顺序一致
                      String interface_name = input.readUTF(); // 请求调用的接口名称
                      System.out.println("调用的服务为:" + interface_name);
                      String method_name = input.readUTF();  // 请求调用的方法名称
                      System.out.println("调用的方法为:" + method_name);
                      Class<?>[] parameterType = (Class<?>[]) input.readObject();  // 被调用方法的参数类型列表
                      Object[] arguments = (Object[]) input.readObject();  // 客户端传过来的参数列表
                      System.out.println("参数列表为 : " + Arrays.toString(arguments));
      
                      // 根据得到的被调用接口名称得到Server的Class
                      Class<?> service_Interface_Class = Class.forName(interface_name);
                      // 根据方法名称和参数类型得到Server的method
                      Method method = service_Interface_Class.getMethod(method_name, parameterType);
                      // 服务实例化
                      Object service = service_Interface_Class.newInstance();
                      // 反射执行这个方法
                      Object result = method.invoke(service, arguments);
      
                      // 写回处理结果
                      out = new ObjectOutputStream(socket.getOutputStream());
                      out.writeObject(result);
      
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }finally {
                  try {
                      out.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
                  if (ss != null){
                      try {
                          ss.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                      ss = null;
                  }
              }
          }
      }
      
  • Client调用:

    • RPC_Client.java
      import java.io.IOException;
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.net.Socket;
      
      public class RPC_Client {
          public void hello(String name){
      
              ObjectOutputStream out = null;
              Socket socket = null;
              try {
                  // 即将调用的接口名
                  String interFaceName = "HelloWorldServiceImpl";
                  // 即将调用的方法名
                  String methodName = "hello";
                  // 参数类型列表
                  Class<?>[] arguments_types = {java.lang.String.class};
                  // 参数
                  Object[] arguments = {name};
      
      
                  socket = new Socket("127.0.0.1", 8090);
      
                  // 将调用的信息写入out
                  out = new ObjectOutputStream(socket.getOutputStream());
                  out.writeUTF(interFaceName);
                  out.writeUTF(methodName);
                  out.writeObject(arguments_types);
                  out.writeObject(arguments);
      
                  // 获取结果
                  ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
                  Object result = in.readObject();
      
                  System.out.println(result.toString());
      
              } catch (Exception e) {
                  e.printStackTrace();
              }finally {
                  try {
                      out.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      
          public static void main(String[] args){
              RPC_Client c = new RPC_Client();
              c.hello("RPC");
          }
      }
      
  • 测试结果:
    • Server端:

      Server端
    • Client端:

      Client端

过程分析:

在这个简单的Demo中,Client端远程调用Server端的hello() 方法,其内部操作大致如下:

  1. 启动RPC_Server,服务器端开始监听本机的8090端口;

  2. 启动RPC_Client,客户端将即将调用方法的相关信息(类名、方法名、参数类型列表、参数列表)写入流,通过新建的socket通过8090端口传到服务器;

  3. 服务器端检测到有客户端调用本机提供的服务,从socket获取信息;

  4. 根据获取到的信息,调用本机对应的服务,执行远程过程;

  5. 服务器端将执行结果写入流,通过socket传回客户端;

  6. 客户端接受服务器端返回的执行结果。


总结:

本文只是RPC功能的简单实现,效率较慢,还存在着很多可以改进的方面:

比如:

  • 通信机制:本文采用的通信方式socket是基于BIO实现的,I/O效率不高,可以使用NIO或直接使用Netty代替BIO实现;

  • 数据传输:本文数据传输速率较慢,可以采用基于JSON数据传输的方式;

  • 序列化:在服务器端与客户端进行通信时,需要将对象名称、方法名称、参数等信息转换成二进制传输,本文采用的是Java原生的readUTF(),writeUTF()等序列化机制,占内存太多,运行效率也不高,可以使用其他开源的序列化机制,如:Hadoop Avro与Google protobuf等;

另外:

  • 本次实现只是一个客户端调用服务端的服务,如果有多个客户端同时调用,就要考虑多线程、多进程的方式,同时如何区分不同客户端,如何将执行结果返回正确的对应客户端,也是需要考虑的问题了。

可见:要实现完善的RPC功能或者实现一个RPC框架需要考虑的问题很多,希望这篇文章对读者有所启发,大家有什么看法欢迎在下方评论。


参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值