【分布式理论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 对应模型的安…...

Docker 部署 redis | 国内阿里镜像
一、简易单机版 1、镜像拉取 # docker hub 镜像 docker pull redis:7.0.4-bullseye # 阿里云镜像 docker pull alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/redis_optimized:20240221-6.2.7-2.3.0 2、运行镜像 docker run -itd --name redis \n …...

48V电气架构全面科普和解析:下一代智能电动汽车核心驱动
48V电气架构:下一代智能电动汽车核心驱动 随着全球汽车产业迈入电动化、智能化的新时代,传统12V电气系统逐渐暴露出其无法满足现代高功率需求的不足。在此背景下,48V电气架构应运而生,成为现代电动汽车(EV)…...

滤波器截止频率的计算
1、RC低通滤波器 图1.1 RC低通滤波器 RC低通滤波器如图1.1所示,电阻R串联电容C,输入电压记为Ui ,输出电压记为Uo。 电容容抗记为,其中ω 2πf。 根据串联分压,列出传递函数。 将①式最右侧的分子与分母各乘以1-jω…...

服务器绑定 127.0.0.1 和 0.0.0.0 的区别
前言 IP 地址实际上并不是分配给计算机的,而是分配给网卡的,因此当计算机上存在多块网卡时,每一块网卡都会有自己的 IP 地址。 绑定 127.0.0.1 是绑定到 lookback 这个虚拟的本地回环接口,该接口只处理本机上的数据,…...

DeepSeek提示词手册
一、核心原则:基于DeepSeek的推理特性 自然语言优先undefinedDeepSeek擅长理解自然表达,无需复杂模板。例如: ❌旧模板:"你是专业分析师,需分三步回答,第一步…" ✅高效提问:"…...

校园网规划方案
个人博客站—运维鹿: http://www.kervin24.top CSDN博客—做个超努力的小奚: https://blog.csdn.net/qq_52914969?typeblog 本课程设计参考学习计算机网络 思科Cisco Packet Tracer仿真实验_哔哩哔哩_bilibili, 文章和pkg详见个人博客站: http://www.kervin24.to…...

python怎么求 一个数是否包含3
python求一个数包含3的方法: 1、使用“for i in 列表名”循环遍历列表中的每一个元素并将每个元素用str()函数转换成字符串格式 2、用“if str(3) in i”判断该元素中是否含有3 完整代码如下: 执行结果如下:...

ARM RFEIA指令作用
FreeRTOS第一个任务如何run起来的 在给ARM cortex R5适配FreeRTOS的过程中,在执行第一个task时,都是使用vTaskStartScheduler()函数,把第一个task运行起来的,其中比较关键在port.c实现的xPortStartScheduler()函数中,…...

【Kubernetes】常用命令全解析:从入门到实战(上)
🐇明明跟你说过:个人主页 🏅个人专栏:《Kubernetes航线图:从船长到K8s掌舵者》 🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、Kubernetes简介 2、安装Kubernetes …...

项目实战(11)-双通道气体压力计V1.0
一. 产品简介: 1、项目背景是在实际应用中需要监控通道内气体的压力,压力计分为两个通道;通道一时实时监控;通道二是保压,设定保压值得上下限后通道内得气体压力值会一直保持在这个范围内。 二. 应用场景:…...