当前位置: 首页 > news >正文

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{@Overridepublic 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;}}
}

查看其代理类:

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());}}
}

对上述代码精简一下:

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());}}
}
super(var1);//查看一下这个函数,如下protected InvocationHandler h;protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h = h;}
    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);}}

其他动态代理的解决方案

小结

相关文章:

RPC通信原理(一)

RPC通信原理 RPC的概念 如果现在我有一个电商项目,用户要查询订单,自然而然是通过Service接口来调用订单的实现类。 我们把用户模块和订单模块都放在一起,打包成一个war包,然后再tomcat上运行,tomcat占有一个进程&am…...

修改/etc/resolve.conf重启NetworkManager之后自动还原

我ping 百度报错: [rootk8snode1 ~]# ping baidu.com ping: baidu.com: Name or service not known很明显,这是DNS解析问题。 于是我修改 /etc/resolv.conf 文件后,执行完sudo systemctl restart NetworkManager,/etc/resolv.con…...

Web前端依赖版本管理最佳实践

本文需要读者懂一点点前端的构建知识: 1. package.json文件的作用之一是管理外部依赖;2. .npmrc是npm命令默认配置,放在工程根目录。 Web前端构建一直都是一个不难,但是非常烦人的问题,在DevOps、CI/CD领域。 烦人的是…...

多线程进阶

一.常见的锁策略 这里所讲的锁,不是一把具体的锁,而是锁的特性 1.乐观锁和悲观锁 悲观乐观是对锁冲突大小的预测 若预测锁冲突概率不大,就可能会少一些工作,那就是乐观锁;反之就是悲观锁 总是假设最坏的情况&…...

总结linux常用命令

Linux常用命令总结如下: 文件与目录操作: ls:列出目录内容cd:改变当前目录pwd:显示当前工作目录mkdir:创建新目录cp:复制文件或目录rm:删除文件或目录mv:移动或重命名文件…...

C++ 枚举

C 枚举 5.4.1普通枚举 枚举的定义:,枚举类型是通过enum关键字定义的,比如定义颜色类型 enum Color {RED, // 默认值为0GREEN, // 默认值为1BLUE // 默认值为2 }; Color myColor RED;注意: (1)括…...

Vue2在一个页面内动态切换菜单显示对应的路由组件

项目的需求是在一个页面内动态获取导航菜单,导航菜单切换的时候显示对应的路由页面,类似于tab切换的形式,切换的导航菜单和页面左侧导航菜单是同一个路由组件,只是放到了一个页面上,显示的个数不同,所有是动…...

执行任务赚积分C卷(JavaPythonC++Node.jsC语言)

现有N个任务需要处理,同一时间只能处理一个任务,处理每个任务所需要的时间固定为1。 每个任务都有最晚处理时间限制和积分值,在最晚处理时间点之前处理完成任务才可获得对应的积分奖励。 可用于处理任务的时间有限,请问在有限的时间内,可获得的最多积分。 输入描述 第一…...

接口测试之文件下载

在工作中对于下载接口,经常会有这样的疑问:这类接口一般功能比较稳定,但是又比较重要,需要占用回归测试时间,有没有可替代的方式? 答案肯定是有的,可以从接口测试/UI自动化测试介入&#xff0c…...

算法思想总结:双指针算法

一、移动零 . - 力扣&#xff08;LeetCode&#xff09; 移动零 该题重要信息&#xff1a;1、保持非0元素的相对位置。2、原地对数组进行操作 思路&#xff1a;双指针算法 class Solution { public:void moveZeroes(vector<int>& nums){int nnums.size();for(int cur…...

python中的zip函数

1.zip&#xff08;&#xff09;同时迭代多个列表、字典等 使用zip()可以同时迭代多个可迭代对象&#xff0c;如列表、字典。 注意&#xff1a;当若干个可迭代对象的长度不相等时&#xff0c;zip()函数会停止在最短的可迭代对象。 例子&#xff1a; # 定义可迭代对象 numbers …...

Element 选择季度组件

<template><el-dialogtitle"选择季度":show-close"false":close-on-click-modal"false":close-on-press-escape"false":visible"visiable"class"dialog list"append-to-body><div><div>&…...

4.MongoDB中16个常用CURD

基本的CURD 作为一个非专业的DBA&#xff0c;我们只需要会一些基本的curd就行&#xff0c;专业的内容还是需要专业的人去干的。CRUD 也就是增删改查&#xff0c;这是数据库最基本的功能&#xff0c;查询还支持全文检索&#xff0c;GEO 地理位置查询等。 01创建库 无需单独创…...

Tomcat数据源笔记

Tomcat数据源笔记 连接池的概念 连接池是一种由容器提供的机制&#xff0c;用于管理数据库连接对象的集合。连接池的主要作用是在应用程序需要与数据库进行交互时&#xff0c;提供可复用的连接对象&#xff0c;从而减少每次建立数据库连接的开销。 连接池的工作原理 连接池的…...

Spring-Kafka笔记整理

引入依赖<dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId> </dependency>配置application.propertiesspring.kafka.bootstrap-servers192.168.99.51:9092编写kafka的配置类Configuration …...

已解决org.apache.hadoop.hdfs.protocol.QuotaExceededException异常的正确解决方法,亲测有效!!!

已解决org.apache.hadoop.hdfs.protocol.QuotaExceededException异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 报错原因 解决思路 解决方法 总结 博主v&#xff1a;XiaoMing_Java 问题分析 在使用Hadoop分布式文件系统&a…...

GitHub打不开的解决方案(超简单)

在国内&#xff0c;github官网经常面临打不开或访问极慢的问题&#xff0c;不挂VPN&#xff08;梯子&#xff0c;飞机&#xff0c;魔法&#xff09;使用体验极差&#xff0c;那有什么好办法解决github官网访问不了的问题&#xff1f;今天小布教你几招轻松访问github官网。 git…...

Unity开发一个FPS游戏之二

在之前的文章中,我介绍了如何开发一个FPS游戏,添加一个第一人称的主角,并设置武器。现在我将继续完善这个游戏,打算添加敌人,实现其智能寻找玩家并进行对抗。完成的效果如下: fps_enemy_demo 下载资源 首先是设计敌人,我们可以在网上找到一些好的免费素材,例如在Unity…...

STM32F103 CubeMX 使用USB生成鼠标设备

STM32F103 CubeMX 使用USB生成鼠标设备 1 配置cubeMX1.1配置外部晶振&#xff0c;配置debug口1.2 配置USB1.3 配置芯片的时钟1.4 生成工程 2. 编写代码2.1 添加申明2.2 main函数代码 1 配置cubeMX 1.1配置外部晶振&#xff0c;配置debug口 1.2 配置USB 1.3 配置芯片的时钟 需…...

HJXH-E1/U静态信号继电器 面板安装 辅助电源220VDC 启动电压220VDC JOSEF约瑟

HJXH系列静态信号继电器 HJXH-61/U静态信号继电器&#xff1b; HJXH-61/I静态信号继电器&#xff1b; HJXH-62/U静态信号继电器&#xff1b; HJXH-62/I静态信号继电器&#xff1b; HJXH-E1/U静态信号继电器&#xff1b; HJXH-E1/I静态信号继电器&#xff1b; HJXH-E2/U静态信号…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...