• 【RPC框架、RPC框架必会的基本知识、手写一个RPC框架案例、优秀的RPC框架Dubbo、Dubbo和SpringCloud框架比较】


    一.RPC框架必会的基本知识

    1.1 什么是RPC?

    RPC(Remote Procedure Call ——远程过程调用),它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络的技术。

    一次完整的RPC同步调用流程:

    1. 服务消费方,也就是我们常说的客户端,以本地调用方式调用客户端存根;
    • 什么叫客户端存根?就是远程方法在本地的模拟对象,一样的也有方法名,也有方法参数,client stub接收到调用后负责将方法名、方法的参数等包装,并将包装后的信息通过网络发送到服务端;
    1. 服务端收到消息后,交给代理存根在服务器的部分后进行解码为实际的方法名和参数;

    2. server stub根据解码结果调用服务器上本地的实际服务;

    3. 本地服务执行并将结果返回给server stub;

    4. server stub将返回结果打包成消息并发送至消费方;

    5. client stub接收到消息,并进行解码;

    6. 服务消费方得到最终结果。

    RPC框架的目标就是要将上面的这些中间步骤都封装起来,当我们进行远程方法调用的时候感觉就像在本地调用方法一样。

    1.2 为什么要有RPC?

    1. 我们最开始开发的时候,一个应用一台机器,将所有功能都写在一起,比如说比较常见的电商场景。

    2. 随着我们业务的发展,我们需要提示性能了,我就就需要将不同的业务功能放到线程里来实现异步和提升性能。

    3. 但是业务越来越复杂,业务量越来越大,单个应用或者一台机器的资源是运行不起来我们的服务的,这个时候,我们就需要将核心业务抽取出来,作为独立的服务,放到其他服务器上或者形成集群。这个时候就会请出RPC,系统变为分布式的架构。

    4. 为什么说千万级流量分布式、微服务架构必备的RPC框架?和LocalCall的代码进行比较,因为引入RPC框架对我们现有的代码影响最小,同时又可以帮我们实现架构上的扩展。现在的开源RPC框架主要有dubbo,grpc等等。

    5. 当服务越来越多,各种RPC之间的调用会越来越复杂,这个时候我们会引入中间件,比如说MQ、缓存,同时架构上整体往微服务去迁移,引入了各种比如容器技术docker,DevOps等等。以此来抗住千万级流量,RPC起到关键的作用。

    1.3 RPC和HTTP

    很多人都容易弄混的一个概念就是总会拿RPC和HTTP作比较,其实RPC和HTTP是完全两个不同层级的东西,他们之间并没有什么可比性。

    1. RPC是远程过程调用,只是对不同应用间相互调用的一种描述,它是一种思想。具体怎么调用?实现方式可以是最直接的TCP通信,也可以是HTTP方式。
    2. 而我们的HTTP是应用层的一种协议,负责服务之间的通信操作。
    3. dubbo是基于tcp通信的,gRPC是Google公布的开源软件,基于最新的HTTP2.0协议,底层使用到了Netty框架的支持。
    1.4 手动实现一个RPC框架需要考虑哪些方面
    1. 代理问题(代理设计模式【动态代理、静态代理】)
    • 代理本质上是要解决的是被调用的服务本质上是远程的服务,但是调用者不知道也不关心,调用者只要结果,具体的事情由代理的那个对象来负责这件事。既然是远程代理,当然是要用代理模式了。
    • 代理(Proxy)是一种设计模式,即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。那我们这里额外的功能操作是干什么,通过网络访问远程服务。
    • jdk的代理有两种实现方式:静态代理和动态代理。
    1. 服务的注册与发现
    • 登记的服务有可能在我们的系统中就是一个名字,怎么变成实际执行的对象实例,当然是使用反射机制。
    • 反射机制是什么?反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
    • 反射机制能做什么?在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
    1. 序列化问题
    • 当我们的方法调用,有方法名,方法参数,这些可能是字符串,可能是我们自己定义的java的类,但是在网络上传输或者保存在硬盘的时候,网络或者硬盘并不认得什么字符串或者Bean对象,它只认得二进制的01串,怎么办?要进行序列化,网络传输后要进行实际调用,就要把二进制的01串变回我们实际的java的类,这个叫反序列化。
    • java里已经为我们提供了相关的机制Serializable。但是序列化与反序列化的效率比较低,我们可以选择fastjson这些框架。
    1. 核心的就是上面的三点,然后还有很多其它方面,需要我们单独的进行考虑…

    二.手写一个RPC框架案例

    2.1 客户端实现代码

    public class Client{
        //远程调用类
        public static Object getStub(final Class clazz) throws Exception{
            InvocationHandler handler = new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Socket socket = new Socket("127.0.0.1", 8888);
                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                    //TODO 送入的class 灵活了
                    String className = clazz.getName();
                    String methodName = method.getName();
                    Class[] parametersTypes = method.getParameterTypes();
                    //TODO 传递class到服务器 , 我自己定义的协议(className|methodName|parametersTypes|args)
                    oos.writeUTF(className);
                    oos.writeUTF(methodName);
                    oos.writeObject(parametersTypes);
                    oos.writeObject(args);
                    oos.flush();
                    //TODO 返回对象
                    ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                    Object o = ois.readObject();
                    oos.close();
                    socket.close();
                    return o ;
                }
            };
            Object o = Proxy.newProxyInstance(clazz.getClassLoader(),
                    new Class[]{clazz},handler);
            return o;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3.2 服务端实现代代码

    /**
     * 服务端:服务更灵活-提供多个类、多个方法的远程接口调用
     */
    public class Server {
        private static boolean running = true;
        public static void main(String[] args) throws  Exception{
            ServerSocket serverSocket = new ServerSocket(8888);
            while (running){
                Socket socket = serverSocket.accept();
                process(socket);
                socket.close();
            }
            serverSocket.close();
        }
        private static void  process(Socket socket) throws Exception{
            InputStream in = socket.getInputStream();
            OutputStream out = socket.getOutputStream();
            ObjectInputStream ois = new ObjectInputStream(in);
    
            //我自己定义的协议(className|methodName|parametersTypes|args)
            //TODO 拿到客户端传递过来的class
            String clazzName =ois.readUTF();
            String methodName =ois.readUTF();
            Class[] parameterTypes = (Class[])ois.readObject();
            Object[] args =(Object[])ois.readObject();
            //反射拿到class
            //通过反射可以获取所有的接口类
            Class clazz =Class.forName(clazzName);
            if(clazz.isInterface()){
                if(clazzName.equals("com.ljw.test.UserService")){
                    clazz = UserServiceImpl.class;
                }
            }
            Method method = clazz.getMethod(methodName,parameterTypes);
            Object object = method.invoke(clazz.newInstance(),args);
            //TODO 返回值:使用对象进行返回
            ObjectOutputStream oos = new ObjectOutputStream(out);
            oos.writeObject(object);
            oos.flush();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    服务端业务逻辑接口

    public interface UserService {
        public User findUserByID(Integer id);
        public User findUserByID2(Integer id);
    }
    
    • 1
    • 2
    • 3
    • 4

    业务逻辑的实现类

    public class UserServiceImpl implements UserService {
        @Override
        public User findUserByID(Integer id) {
            return new User(id,"ljw");
        }
        @Override
        public User findUserByID2(Integer id) {
            return new User(id,"硕风和炜");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    测试代码

    public class Main{
    
        public static void main(String[] args) throws Exception{
            //RPC调用
            IUserService service=(IUserService)Client.getStub(UserService.class);
            User user = service.findUserByID(1314);
            System.out.println(user.getName());
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    三.优秀的RPC框架Dubbo

    3.1 Dubbo框架

    可以先看一个之前写过的一篇文章,需要的自行查看,接下来继续补充完善一些关于Dubbo框架。
    【RPC框架之Dubbo的这些面试知识你知道吗?Dubbo是什么?RPC?底层通信框架?与SpringCloud区别?服务注册与发现流程?核心组件有哪些?注册中心挂掉,生产者消费者通信吗?】

    image.png

    3.2 Dubbo和SpringCloud
    1. 协议上:http相对更规范,更标准,更通用,无论哪种语言都支持http协议。现在开源中间件,基本最先支持的几个协议都包含RESTful。

    2. RPC协议性能要高的多,例如Protobuf、Thrift、Kyro等,(如果算上序列化)吞吐量大概能达到http的二倍。响应时间也更为出色。千万不要小看这点性能损耗,公认的,微服务做的比较好的,例如,netflix、阿里,曾经都传出过为了提升性能而合并服务。

    3. 服务全面上比较:当然是springloud更胜一筹,但也就意味着在使用springloud上其实更重量级一点,dubbo目前版本专注于服务治理,使用上更轻量一点。

    4. 就国内的热度来说,dubbo相比起来还略胜一筹。
      通过百度指数查看,感兴趣的可以自行查看
      在这里插入图片描述

    总的来说对外开放的服务推荐采用RESTFUL,内部调用推荐采用RPC方式。也需要我们面对不同的情况具体分析。

    好了,到这里【RPC框架、RPC框架必会的基本知识、手写一个RPC框架案例、优秀的RPC框架Dubbo、Dubbo和SpringCloud框架比较】就结束了,关于Dubbo更多的内容,持续更新创作中。

  • 相关阅读:
    Langchain-chatchat本地部署
    不同类型的软件企业该如何有效的管理好你的软件测试团队?
    【网络编程】TCP传输控制协议(Transmission Control Protocol)
    计算机毕业设计Java网上汽车售票系统(源码+系统+mysql数据库+Lw文档)
    海底世界-第11届蓝桥杯Scratch省赛真题第3题
    修过书上的建网站用的CMS程序源码,增加在文章中插入图片功能
    Spring事务2+银行转账拓展
    ZooKeeper
    Jmeter连接Mysql数据库
    Spring Boot与Netty:构建高性能的网络应用
  • 原文地址:https://blog.csdn.net/Coder_ljw/article/details/127825084