Dubbo之RPC框架

Posted yzfree星球

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo之RPC框架相关的知识,希望对你有一定的参考价值。

上一篇简单介绍了一下Dubbo,Dubbo首先是一个RPC框架,那么这篇我们先从RPC聊起 。

Dubbo是一个基于Java的RPC框架,底层基于高性能通信框架Netty实现。RPC(Remote Procedure Call)翻译过来就是远程过程调用,那么什么是远程过程调用呢?我们应该知道本地过程调用,就像我们本地的A方法调用B方法就是本地过程调用。那么对应的远程过程调用,就是通过网络去远程计算机服务上请求服务,至于 RPC 要如何调用远程计算机上的方法可以走 HTTP协议、也可以是基于 TCP 或者UDP自定义协议。

最开始接触RPC的时候我想当然的把RPC和HTTP来进行对比,现在看来这两个都不是一个层级的东西,不能瞎比。


而 RPC 框架的作用,就是让我们使用远程调用像本地调用一样简单方便,并且能解决一些远程调用中会发生的问题。让用户像调用本地方法一样无感知、放心的使用,不需要了解底层网络的通信机制


  • 那么如何设计一个RPC框架

    通过上面我们了解到什么是RPC以及RPC框架的作用是什么,那么接下来刁钻的面试官肯定得问你怎么设计一个RPC框架,没想到吧,我做了准备。


RPC框架有以下三个核心角色:


1.服务提供者

服务提供者需要提供接口的实现,然后把接口暴露出去,将自己的服务信息注册到注册中心。并定期像注册中心发送心跳,证明自己还活着。

这样服务消费者请求过来之后,要实现反序列化,序列化完成之后的请求放入到线程池,某个线程拿到这个请求去执行对应代码的逻辑,然后返回结果给服务消费者。

2.服务消费者

服务消费者启动时向注册中心订阅所需要的服务,并缓存到实例列表中。

服务消费者要去消费服务提供者提供的服务,双方就要约定一个协议,比如用http协议来进行通信。调用时,从实例列表中选择一个服务实例去调用。

因为是网络传输,所以序列化是必须的,比如代码中的类信息需要经过序列化之后才能通过网络传输,因此要约定序列化格式。

因为服务消费者是直接调用服务提供者,就算注册中心宕机了也不会影响已经正常运行的消费者和服务者。

3.注册中心

注册中心就像一个平台,用于服务提供者暴露服务,服务消费者在上面发现服务。服务实例列表发生变化时,动态通知消费者,消费者更新本地的实例列表。

这个平台还可以用作配置中心,将所需配置集中存放到此,配置变更之后可以动态通知消费者。

市面上常用的注册中心有:zookeeper,nacos,eureka,consul




  • RPC远程调用过程



总结起来就是这么回事:

  1. 服务调用者以本地调用方式调用方法;

  2. client stub将打包后的消息通过网络发送至服务提供者;

  3. server stub收到消息后进行解码,对应在Java中就是反序列化过程;

  4. server stub根据解码结果调用本地的方法

  5. 本地服务执行处理逻辑并将结果返回给server stub;

  6. server stub将返回结果打包成消息,对应Java里的序列化过程;

  7. server stub将打包后的消息通过网络发送至服务消费者;

  8. client stub接收到消息并解码,对应 Java中的反序列化过程;

  9. 服务调用者得到最终返回结果;



  • 简简单单实现一个RPC框架

    服务提供者相关代码:

 private static void runRPCServer(Object service, int port) {
ServerSocket server = null; try { server = new ServerSocket(port); System.out.println("服务提供者启动!!"); while (true) { final Socket accept = server.accept(); System.out.println("收到请求并放入线程池执行"); pool.submit(new Runnable() { public void run() { ObjectInputStream input = null; ObjectOutputStream output = null; try { //1.反序列化 input = new ObjectInputStream(accept.getInputStream()); //2.获得方法名 String methodName = input.readUTF(); //3.获得参数类型 Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); //4.获得参数 Object[] args = (Object[]) input.readObject(); //5.根据方法名找到方法 Method method = service.getClass().getMethod(methodName, parameterTypes); //6.执行对应的方法 Object result = method.invoke(service, args); //7.从监听的socket中获得输出流 output = new ObjectOutputStream(accept.getOutputStream()); //8.返回执行结果 output.writeObject(result); } catch (Exception e) { e.printStackTrace(); } } }); }
} catch (IOException e) { e.printStackTrace(); } finally { try { if (null != server) { server.close(); } } catch (IOException e) { e.printStackTrace(); } } }

服务消费者相关代码:

 public static Object getClientService(Class interfaceClazz) {
return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz}, (proxy, method, args) -> callRPCServer(method,args));
}
private static Object callRPCServer(Method method, Object[] args){ Object back = null; //默认ip:127.0.0.1 String ip = "127.0.0.1"; //端口号默认设80 int port = 8090; ObjectOutputStream objOutStream = null; Socket client; try { //指定服务提供者ip和端口 client = new Socket(ip,port);
objOutStream = new ObjectOutputStream(client.getOutputStream()); //方法名 objOutStream.writeUTF(method.getName()); //参数类型 objOutStream.writeObject(method.getParameterTypes()); //参数值 objOutStream.writeObject(args);
try (ObjectInputStream objInStream = new ObjectInputStream(client.getInputStream())) { //读取远程方法返回的结果 back = objInStream.readObject();
} catch (ClassNotFoundException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(null != objOutStream) objOutStream.close(); } catch (IOException e) { e.printStackTrace(); } } return back; }

以上便是一个非常简陋的rpc框架demon,就是服务消费者传递了方法名、参数类型和参数值,服务提供者接收到这样参数之后调用对应的方法并返回结果。

以上是关于Dubbo之RPC框架的主要内容,如果未能解决你的问题,请参考以下文章

Dubbo之RPC框架

稳定性 耗时 监控原因分析-- dubbo rpc 框架 的线程池,io 连接模型. 客户端,服务端

Netty框架之协议应用二(RPC开发实战之Dubbo)

Netty框架之协议应用二(RPC开发实战之Dubbo)

Netty框架之协议应用二(RPC开发实战之Dubbo)

大牛带你深入Dubbo,高性能RPC通信框架:Dubbo简介和总体大图