• RPC通信原理(一)


    RPC通信原理

    RPC的概念

    如果现在我有一个电商项目,用户要查询订单,自然而然是通过Service接口来调用订单的实现类。

    我们把用户模块和订单模块都放在一起,打包成一个war包,然后再tomcat上运行,tomcat占有一个进程,这个项目也是在这个进程中运行的,模块之间的调用也是在进程的本地进行调用,那么如果我是一个分布式项目该怎么解决呢?

    现在用户和订单模块部署在两台服务器上,这时候用户模块就不能直接调用订单模块了,只能够通过网络来进行调用,而RPC就是用来干这个事情的,通过RPC来进行服务之间的远程调用

    特点:

    • 把远程实现搬到了本地,效果上远程调用和本地调用没区别
    • 这里的搬运到了本地不是真的把代码CV到调用者本地,而是RPC的一种无侵入式的抽象,调用者可以调用本地服务一样去调用远程的服务,其中的网络协议,数据传输等实现细节被RPC屏蔽掉。
    • 使用CS模式,客户端发起请求并传递参数,服务端接收参数后执行,并将执行结果返回
    • 底层网络通信细节对上层开发者屏蔽,上层开发者无需为这一交互过程做额外的编码,做到应用无侵入

    RPC的应用场景有哪些?

    需要远程通信的各类场景

    小结

    RPC中的关键技术点

    调用过程如下:

    调用流程总结:

    首先客户端要远程调用这个接口,这个接口会由RPC底层动态代理实现远程调用,然后通过序列化数据,以及使用互相通信的协议编码,通过网络传输到对端服务器上,服务器从网络模块拿到数据,经过解码、反序列化一系列操作之后,调用这个函数的实现方法,把结果再次经过序列化和协议编码处理之后发送给客户端,客户端进行协议解码和反序列化得到数据。

    小结

    RPC中的高级特性

    动态感知并且自动切换

    当服务器压力小的时候可以减少RPC集群数量,当服务器压力大的时候可以增加RPC集群节点,这一过程都是自动完成的,这一功能依赖于注册中心

    服务发现模块会监听注册中心,看注册中心时候发送消息给服务发现模块,当注册中心的配置发生变化之后,注册中心会像服务发现模块发送消息,告知它配置已经发生了修改,然后去重写拉取配置,写入本地缓存当中。

    小结

    RPC的优势

    Zookeeper的注册原理及存储结构

    Zookeeper是一个树形结构,通过路径+节点来标识一个节点。

    https://www.runoob.com/w3cnote/zookeeper-tutorial.html

    注册中心Dubbo会记录有哪些远程调用的接口,及其生产者和消费者,对于远程调用的接口是一个持久节点,会一直存在,而生产者和消费者却是临时节点,这是因为:

    • 容错与自动摘除

      • 当提供服务的生产者因为故障或正常关闭时,Zookeeper上的临时节点会自动删除。这样消费者(Consumer)从Zookeeper获取的服务列表中将不再包含已下线的服务提供者,实现了服务列表的自动更新和失效剔除。
    • 服务实例生命周期管理

      • 临时节点的存在与Zookeeper客户端会话绑定,当服务提供者的Zookeeper客户端会话中断(例如进程退出、网络断开等情况)时,对应的临时节点就会被清理,这与服务提供者的生命周期保持一致。
    • 负载均衡与集群伸缩性

      • 使用临时节点可以方便地支持集群环境下的动态扩容与缩容,新的服务提供者上线或者旧的提供者下线,都能迅速反映到服务注册中心,进而使得消费者能及时感知并调整访问策略。

    Dubbo协议异步单一长连接原理与优势

    2. 异步单一长连接原理

    2.1 异步通信

    Dubbo协议采用异步通信模型,即客户端发送请求后不需要等待服务端响应,可以立即进行其他操作。这种模型的核心在于使用了NIO(Non-blocking I/O)技术,通过事件驱动和回调机制来实现请求的并发处理。

    具体来说,客户端在发送请求后,将请求信息注册到事件多路复用器上。当服务端响应到达时,事件多路复用器会触发相应的回调函数进行处理。这样一来,客户端就可以异步地处理多个请求,提高了系统的并发能力和吞吐量。

    2.2 单一长连接

    Dubbo协议使用单一长连接的方式来进行通信。所谓单一长连接,就是指客户端与服务端之间只建立一个TCP连接,并保持长时间的有效性。

    这种设计方案有以下几个优势:

    2.2.1 连接复用

    由于只有一个TCP连接,不需要频繁地建立和关闭连接,避免了TCP连接的三次握手和四次挥手的开销。同时,连接的复用还可以减轻网络设备的负担,提高网络的利用率。

    2.2.2 减少资源消耗

    每个TCP连接都会占用一定的系统内存和CPU资源,如果每个请求都需要建立新的连接,那么系统资源开销将会非常大。而采用单一长连接的方式,可以大幅度降低资源的消耗,提高系统的稳定性和可伸缩性。

    2.2.3 保证顺序性

    由于Dubbo协议仅使用一个连接,发送的请求和接收的响应不会交错。这意味着请求和响应可以按照发送的顺序进行处理,不会出现乱序的情况。这在一些有序性要求较高的场景中非常重要。

    3. 异步单一长连接的优势

    异步单一长连接作为Dubbo协议的核心设计,具有以下几个显著的优势:

    3.1 减少网络开销

    采用异步通信模型和单一长连接方式可以减少网络的开销,避免了频繁地建立和关闭连接带来的额外开销。这对于海量请求的场景尤为重要,可以提升系统的性能和吞吐量。

    3.2 提高系统的稳定性和可伸缩性

    由于单一长连接减少了资源消耗,系统的稳定性和可伸缩性得到了提高。在高并发情况下,系统能够更好地承受请求的压力,同时也降低了系统崩溃的风险。

    3.3 简化系统维护和监控

    采用单一长连接的方式简化了系统的维护和监控工作。只需要关注一个TCP连接的状态和性能指标,而不需要管理多个独立的连接。这有助于提高运维效率和降低维护成本。

    3.4 保证请求顺序性

    由于异步通信模型和单一长连接的特性,Dubbo协议能够保证请求和响应的顺序性。这对于某些有序性要求的业务场景非常重要,例如金融交易系统中的订单处理。

    长链接中断的情况:

    1. 服务端或客户端主动断开连接。
    2. 网络故障导致连接中断。
    3. 客户端(如消费方应用)或者服务端进程终止。

    代理技术

    JDK Proxy原理

    //JDK的动态代理
    public class JdkProxyTest {
        public static void main(String[] args) {
         //会将生成的class保存在工程根目录下的 com/sun/proxy 目录里面
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
            BookService bookService=(BookService) Proxy.newProxyInstance(
                    Thread.currentThread().getContextClassLoader(), //类加载器
                    new Class[]{BookService.class}, //要动态代理实现的方法
                    new Handler() //要调用的方法实现
            );
            Book book=bookService.findById("100");
            System.out.println(book);
        }
    
        static class Handler implements InvocationHandler{
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //发起远程网络调用
                System.out.println("模拟发起网络远程调用");
                Book book=new Book();
                book.setId(args[0].toString());
                book.setName("斗破苍穹");
                book.setTitle("玄幻");
                book.setTag("玄幻");
                book.setContent("爽文");
                return book;
            }
        }
    }
    
    • 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

    查看其代理类:

    public final class $Proxy0 extends Proxy implements BookService {
        private static Method m1;
        private static Method m2;
        private static Method m0;
        private static Method m3;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final Book findById(String var1) throws  {
            try {
                return (Book)super.h.invoke(this, m3, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
                m3 = Class.forName("BookService").getMethod("findById", Class.forName("java.lang.String"));
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    对上述代码精简一下:

    public final class $Proxy0 extends Proxy implements BookService {
        private static Method m1;
        private static Method m2;
        private static Method m0;
        private static Method m3; //findById方法
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final Book findById(String var1) throws  {
            try {
                return (Book)super.h.invoke(this, m3, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
                m3 = Class.forName("BookService").getMethod("findById", Class.forName("java.lang.String"));
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    • 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
    super(var1);//查看一下这个函数,如下
    
    	protected InvocationHandler h;
    
        protected Proxy(InvocationHandler h) {
            Objects.requireNonNull(h);
            this.h = h;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
        public final Book findById(String var1) throws  {
            try {
                return (Book)super.h.invoke(this, m3, new Object[]{var1}); //这里的h就是我们传递过来的Handler方法,也就是直接调用我们自己实现的handler方法
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    其他动态代理的解决方案

    小结

  • 相关阅读:
    数字电子技术笔记——组合逻辑功能
    达人评测i7 12700kf和i9 12900kf选哪个好
    【TensorFlow1.X】系列学习笔记【基础一】
    FATAL ERROR: Received unexpected end-of-file from SFTP server
    arm day 6
    内核中oops 错误解析以及问题定位
    第1章 电子设计与制作基础
    【wavesurfer.js实战范例】多区域音频标注(含区域实时切换显示)
    字节的面试题到底有多难?大厂为何都注重算法?我们该如何应对?
    微前端四:qiankun在开发中遇到的问题
  • 原文地址:https://blog.csdn.net/2301_79516932/article/details/136656351