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接收到调用后负责将方法名、参数、参数类型等组装成能够进行网络传输的消息体;其实这一步在Java中就是序列化过程。然后找到对应的服务地址,并将消息通过网络发送到服务端;

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

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

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

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

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

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

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

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

  • 简简单单实现一个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,就是服务消费者传递了方法名、参数类型和参数值,服务提供者接收到这样参数之后调用对应的方法并返回结果。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的架狗师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值