【分布式理论7】分布式调用之:服务间的(RPC)远程调用
文章目录
- 一、RPC 调用过程
- 二、RPC 动态代理:屏蔽远程通讯细节
- 1. 动态代理示例
- 2. 如何将动态代理应用于 RPC
- 三、RPC序列化与协议编码
- 1. RPC 序列化
- 2. RPC 协议编码
- 2.1. 协议编码的作用
- 2.2. RPC 协议消息组成
- 四、RPC 网络传输
- 1. 网络传输流程
- 2. 关键优化点
一、RPC 调用过程
RPC(Remote Procedure Call,远程过程调用)是一种让不同网络节点上的服务相互调用的技术。它的核心目标是屏蔽远程调用的复杂性,使远程服务的调用方式如同本地调用一样简单。在分布式系统中,RPC 通过封装底层网络通信细节,提高了服务调用的可用性和开发效率。
RPC 调用流程包括:

- 动态代理:客户端通过代理对象调用远程方法。
- 序列化:将请求数据转换为二进制格式,便于传输。
- 协议编码:增加数据包的协议标识和长度信息。
- 网络传输:通过网络传递数据包。
- 协议解码:服务端解析请求包。
- 反序列化:将二进制数据转换回原始对象。
- 执行方法:调用对应的远程方法并处理请求。
- 响应返回:按照相同的序列化、网络传输等流程将响应结果返回给调用方。
二、RPC 动态代理:屏蔽远程通讯细节
动态代理(Dynamic Proxy)是 Java 提供的一种机制,允许在运行时动态创建代理对象,拦截方法调用,并在调用前后执行额外的逻辑。
在 RPC 场景中,动态代理的主要作用是屏蔽底层的远程通信细节,让客户端可以像调用本地方法一样调用远程服务。
1. 动态代理示例
示例代码:
public interface ServerProvider {void sayHello(String str);
}public class ServerProviderImpl implements ServerProvider {@Overridepublic void sayHello(String str) {System.out.println("Hello " + str);}
}import java.lang.reflect.*;/**
- `DynamicProxy` 实现了 `InvocationHandler`,用于拦截方法调用并执行代理逻辑。
- `invoke` 方法中:1. `method.invoke(realObject, args);` 通过反射调用真实对象的方法。
*/
public class DynamicProxy implements InvocationHandler {private Object realObject;public DynamicProxy(Object object) {this.realObject = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return method.invoke(realObject, args);}
}public class Client {public static void main(String[] args) {ServerProvider realServer = new ServerProviderImpl();InvocationHandler handler = new DynamicProxy(realServer);ServerProvider proxyInstance = (ServerProvider) Proxy.newProxyInstance(handler.getClass().getClassLoader(),realServer.getClass().getInterfaces(),handler);proxyInstance.sayHello("world");}
}
通过动态代理,客户端不直接依赖于 ServerProviderImpl,而是通过接口和代理类进行调用,这样:
- 解耦了客户端和服务端,不需要在客户端硬编码调用远程方法。
- 方便在代理类中加入 RPC 逻辑,比如序列化、网络传输等。
- 增强扩展性,可以在
invoke方法中添加日志、权限校验、负载均衡等功能。
2. 如何将动态代理应用于 RPC
(1) 在代理类中加入远程调用逻辑
(2) 客户端使用代理调用远程服务
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 1. 构造 RPC 请求RpcRequest request = new RpcRequest();request.setMethodName(method.getName());request.setParameters(args);// 2. 发送请求到远程服务RpcResponse response = RpcClient.sendRequest(request);// 3. 解析响应并返回结果return response.getResult();
}ServerProvider serverProvider = (ServerProvider) Proxy.newProxyInstance(getClass().getClassLoader(),new Class[]{ServerProvider.class},new RpcDynamicProxy("http://remote-server")
);
serverProvider.sayHello("world");
如果想进一步实现 RPC 的完整流程,可以加入序列化、网络传输、反序列化等模块,搭建一个真正的 RPC 组件!
三、RPC序列化与协议编码
1. RPC 序列化
序列化是将对象转换成字节流的过程,而反序列化则是恢复对象的过程。常见的序列化方式包括:
- JSON:易读易用,但额外空间开销较大。
- Hessian:二进制格式,序列化后字节数小,性能优于 JSON。
- Protobuf:高效、跨语言支持,适用于大规模分布式应用。
- Thrift:Facebook 开源的高效序列化框架,结合了 RPC 服务框架。
2. RPC 协议编码
2.1. 协议编码的作用
有了序列化功能,就可以将客户端的请求对象转化成字节流在网络上传输了,这个字节流转换为二进制信息以后会写入本地的 Socket 中,然后通过网卡发送到服务端。从编程角度来看,每次请求只会发送一个请求包,但是从网络传输的角度来看,网络传输过程中会将二进制包拆分成很多个数据包,这一点也可以从 TCP 传输数据的原理看出。拆分后的多个二进制包会同时发往服务端,服务端接收到这些数据包以后,将它们合并到一起,再进行反序列化以及后面的操作。
实际上,协议编码要做的事情就是对同一次网络请求的数据包进行拆分,并且为拆分得到的每个数据包定义边界、长度等信息。
2.2. RPC 协议消息组成
RPC 协议消息由 消息头 和 消息体 组成:
- 消息头 包含协议标识、数据长度、请求类型等信息。
- 消息体 是序列化后的数据。
协议编码的核心目标是确保数据包正确地分片、合并,并提供必要的描述信息,保障网络传输的可靠性。
消息头部分主要存放消息本身的描述信息,如图所示。

| 名称 | 描述 |
|---|---|
| 魔术位(magic) | 协议魔术,为解码设计 |
| 消息头长度(header size) | 用来描述消息头长度,为扩展设计 |
| 协议版本(version) | 协议版本,用于版本兼容 |
| 消息体序列化类型(st) | 描述消息体的序列化类型,例如 JSON、gRPC |
| 心跳标记(hb) | 每次传输都会建立一个长连接,隔一段时间向接收方发送一次心跳请求,保证对方始终在线 |
| 单向消息标记(ow) | 标记是否为单向消息 |
| 响应消息标记(rp) | 用来标记是请求消息还是响应消息 |
| 响应消息状态码(status code) | 标记响应消息状态码 |
| 保留字段(reserved) | 用于填充消息,保证消息的字节是对齐的 |
| 消息 Id(message id) | 用来唯一确定一个消息的标识 |
| 消息体长度(body size) | 描述消息体的长度 |
四、RPC 网络传输
1. 网络传输流程
在 RPC 调用中,服务调用方(Client)需要发送请求给服务提供方(Server),然后等待服务器处理并返回响应数据。在这个过程中,数据在应用程序、操作系统内核、网络传输三个层次之间流动,并涉及多个数据复制操作。
从示意图中可以看出,数据的流转主要分为两部分:请求发送过程、响应接收过程

请求发送流程(Client -> Server),服务调用方(Client)发起 RPC 请求,其数据流动过程如下:
| 步骤 | 操作 | 数据位置 |
|---|---|---|
| 1 | 应用程序写入数据 | 业务代码执行RPC调用,将数据写入应用缓冲区(User Space) |
| 2 | 数据复制到内核缓冲区 | 操作系统将应用缓冲区的数据复制到内核缓冲区(Kernel Space) |
| 3 | 通过网络发送 | 数据从内核缓冲区被传输到网卡(Network Card),并通过网络协议(如TCP)拆分成数据包发送到远程服务器 |
| 4 | 服务器接收数据 | 服务器端网卡接收数据包,并将其存入内核缓冲区 |
| 5 | 数据复制到应用缓冲区 | 服务器的内核将数据复制到应用缓冲区 |
| 6 | 应用程序读取数据 | 服务器端应用程序从应用缓冲区中获取数据,执行请求逻辑(如数据库查询、业务处理) |
响应接收流程(Server -> Client):服务提供方(Server)处理完请求后,将结果返回给客户端,数据流动过程如下:
| 步骤 | 操作 | 具体内容 |
|---|---|---|
| 7 | 应用程序写入数据 | 服务器应用程序生成响应数据,并写入应用缓冲区 |
| 8 | 数据复制到内核缓冲区 | 服务器操作系统将数据从应用缓冲区复制到内核缓冲区,准备发送 |
| 9 | 通过网络发送 | 服务器的网卡将数据包发送到客户端 |
| 10 | 客户端接收数据 | 客户端网卡接收数据包,操作系统将其存入内核缓冲区 |
| 11 | 数据复制到应用缓冲区 | 数据从内核缓冲区复制到应用缓冲区,以便应用程序使用 |
| 12 | 应用程序读取数据 | 客户端应用程序从应用缓冲区中获取响应数据,完成 RPC 调用 |
通过上面对 RPC 调用流程的描述,可以看出服务调用方需要经过一系列的数据复制,才能通过网络传输将信息发送到服务提供方。此外可以看出网络 IO 传输和数据计算过程存在先后顺序,因此当前者出现延迟时会导致后者处于阻塞。另外,应用程序中存在同步调用和异步调用,因此衍生出了同步阻塞 IO(blocking IO)、同步非阻塞 IO(non-blocking IO)、多路复用 IO(multiplexing IO)这几种 IO 模式。(下篇分析ing)
2. 关键优化点
RPC 网络传输过程涉及多个阶段,包括数据在应用缓冲区、内核缓冲区、网络传输中的流转。优化 RPC 传输的关键在于减少数据复制、优化网络通信、使用异步 I/O 机制,提高整体性能。
| 操作 | 具体内容 |
|---|---|
| 减少数据复制 | 采用 零拷贝(Zero-Copy) 技术,如 mmap、sendfile,避免数据在用户态和内核态之间频繁复制 |
| 优化网络传输 | 使用 长连接(Keep-Alive) 避免频繁建立 TCP 连接;采用 批量发送、数据压缩 来减少数据传输的开销 |
| 异步 I/O 处理 | 使用 异步 I/O(如 Netty、epoll),避免同步阻塞,提高并发处理能力 |
| 优化缓冲区管理 | 采用 池化缓冲区(Buffer Pool) 避免频繁申请和释放内存 |
相关文章:
【分布式理论7】分布式调用之:服务间的(RPC)远程调用
文章目录 一、RPC 调用过程二、RPC 动态代理:屏蔽远程通讯细节1. 动态代理示例2. 如何将动态代理应用于 RPC 三、RPC序列化与协议编码1. RPC 序列化2. RPC 协议编码2.1. 协议编码的作用2.2. RPC 协议消息组成 四、RPC 网络传输1. 网络传输流程2. 关键优化点 一、RPC…...
人工智能应用-智能驾驶精确的目标检测和更高级的路径规划
实现更精确的目标检测和更高级的路径规划策略是自动驾驶领域的核心任务。以下是一个简化的示例,展示如何使用Python和常见的AI库(如TensorFlow、OpenCV和A*算法)来实现这些功能。 1. 环境准备 首先,确保安装了以下库:…...
dynamic_cast和static_cast和const_cast
dynamic_cast 在 C 中的作用 dynamic_cast 是 C 运行时类型转换(RTTI, Run-Time Type Identification)的一部分,主要用于: 安全的多态类型转换检查类型的有效性向下转换(Downcasting)跨类层次的指针或引用…...
DEEPSEEK与GPT等AI技术在机床数据采集与数字化转型中的应用与影响
随着人工智能(AI)技术的迅猛发展,深度学习、自然语言处理等先进技术开始广泛应用于各行各业。在制造业尤其是机床行业,AI技术的融合带来了巨大的变革,尤其在机床数据采集与机床数字化方面的应用。本文将探讨DEEPSEEK、…...
高速存储文章目录
《zynq tcp万兆网和ftp协议分析-CSDN博客》 《国产fpga nvme ip高速存储方案设计_fpga 高速存储-CSDN博客》 《国微pcie switch 8748高速存储方案设计_国产pcie switch-CSDN博客》 《FPGA SATA高速存储设计-CSDN博客》 《FPGA NVME高速存储设计_690t fpga-CSDN博客》 《zy…...
车载测试工具 --- CANoe VH6501 进行Not Acknowledge (NAck) 测试
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…...
【清晰教程】通过Docker为本地DeepSeek-r1部署WebUI界面
【清晰教程】本地部署DeepSeek-r1模型-CSDN博客 目录 安装Docker 配置&检查 Open WebUI 部署Open WebUI 安装Docker 完成本地DeepSeek-r1的部署后【清晰教程】本地部署DeepSeek-r1模型-CSDN博客,通过Docker为本地DeepSeek-r1部署WebUI界面。 访问Docker官…...
Linux运维——用户管理
Linux用户管理 一、Linux用户管理要点二、常用命令2.1、groupadd2.2、groupdel2.3、groupmod2.4、groups2.5、useradd2.6、userdel2.7、passwd2.9、su2.10、sudo2.10.1、给普通用户授权 sudo2.10.2、 免密码授权 sudo 一、Linux用户管理要点 创建用户组 - 使用 groupadd删除用…...
mac下dify+deepseek部署,实现私人知识库
目前deepseek 十分火爆,本地部署实现私有知识库,帮助自己日常工作,上一篇使用工具cherry studio可以做到私人知识库。今天学习了一下,使用Dify链接deepseek,实现私人知识库,也非常不错,这里分享…...
Linux中设置开机运行指令
系统:Debian 12 使用systemd来设置开机自启动脚本或命令是一个更加现代且推荐的方法。下面是具体的步骤: 创建守护脚本 首先,你需要创建一个Shell脚本文件,比如mydaemon.sh,并在其中编写你的守护脚本逻辑。确保这个脚…...
IDEA中列举的是否是SpringBoot的依赖项的全部?在哪里能查到所有依赖项,如何开发自己的依赖项让别人使用
在 IntelliJ IDEA 中列举的依赖项并不一定是 Spring Boot 项目的全部依赖项。IDEA 通常只显示你在 pom.xml(Maven)或 build.gradle(Gradle)中显式声明的依赖项,而这些依赖项本身可能还会引入其他传递性依赖。 1. 如何…...
Ollama命令使用指南
Ollama 命令使用指南 Ollama 命令使用指南1. Ollama 命令概览2. Ollama 命令详解2.1 启动 Ollama2.2 创建模型2.3 查看模型信息2.4 运行模型2.5 停止运行的模型2.6 从注册表拉取模型2.7 推送模型到注册表2.8 列出本地模型2.9 查看正在运行的模型2.10 复制模型2.11 删除模型 3. …...
LIMO:上海交大的工作 “少即是多” LLM 推理
25年2月来自上海交大、SII 和 GAIR 的论文“LIMO: Less is More for Reasoning”。 一个挑战是在大语言模型(LLM)中的复杂推理。虽然传统观点认为复杂的推理任务需要大量的训练数据(通常超过 100,000 个示例),但本文展…...
Android studio怎么创建assets目录
在Android Studio中创建assets文件夹是一个简单的步骤,通常用于存储不需要编译的资源文件,如文本文件、图片、音频等 main文件夹,邮件new->folder-assets folder...
常见的前端框架和库有哪些
1. React 描述:由 Facebook 开发的一个 JavaScript 库,用于构建用户界面,尤其是单页面应用(SPA)。特点: 基于组件的架构,便于重用 UI 组件。使用虚拟 DOM 提升性能。容易与其他库和框架集成。 …...
【批量获取图片信息】批量获取图片尺寸、海拔、分辨率、GPS经纬度、面积、位深度、等图片属性里的详细信息,提取出来后导出表格,基于WPF的详细解决方案
摄影工作室通常会有大量的图片素材,在进行图片整理和分类时,需要知道每张图片的尺寸、分辨率、GPS 经纬度(如果拍摄时记录了)等信息,以便更好地管理图片资源,比如根据图片尺寸和分辨率决定哪些图片适合用于…...
数据结构与算法(test3)
七、查找 1. 看图填空 查找表是由同一类型的数据元素(或记录)构成的集合。例如上图就是一个查找表。 期中(1)是______________. (2)是______________(3)是_____关键字_______。 2. 查找(Searching) 就是根据给定的某个值, 在查…...
基于Python的人工智能驱动基因组变异算法:设计与应用(下)
3.3.2 数据清洗与预处理 在基因组变异分析中,原始数据往往包含各种噪声和不完整信息,数据清洗与预处理是确保分析结果准确性和可靠性的关键步骤。通过 Python 的相关库和工具,可以有效地去除噪声、填补缺失值、标准化数据等,为后续的分析提供高质量的数据基础。 在基因组…...
C++ 顺序表
顺序表的操作有以下: 1 顺序表的元素插入 给定一个索引和元素,这个位置往后的元素位置都要往后移动一次,元素插入的步骤有以下几步 (1)判断插入的位置是否合法,如果不合法则抛出异常 (2&…...
Mac(m1)本地部署deepseek-R1模型
1. 下载安装ollama 直接下载软件,下载完成之后,安装即可,安装完成之后,命令行中可出现ollama命令 2. 在ollama官网查看需要下载的模型下载命令 1. 在官网查看deepseek对应的模型 2. 选择使用电脑配置的模型 3. copy 对应模型的安…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
