【gRPC】什么是RPC——介绍一下RPC
说起RPC,博主使用CPP手搓了一个RPC项目,RPC简单来说,就是远程过程调用:我们一般在本地传入数据进行执行函数,然后返回一个结果;当我们使用RPC之后,我们可以将函数的执行过程放到另外一个服务器上,而不是在本地创建一个函数栈帧,将函数执行所需要的一些东西压入到栈中。下面,我们来学习RPC!!!
一、远程过程调用带来的问题
在远程调用时,我们需要执行的函数体是在远程的机器上,也就是说,add是在另一个进程中执行的。这就带来了几个问题:
1.1 Caller ID 映射
我们怎么告诉远程机器我们要调用add,而不是sub或者 Foo呢??在本地调用中,直接通过函数指针来指定的,我们调用add,编译器就自动帮我们调用它相应的函数指针。但是在远程调用中,指针是不行的,因为两个进程的地址空间是完全不一样的。所以,在RPC中,所有的函数都必须有自己的一个ID。
这个ID在所有进程中都是唯一确定的。客户端在做远程调用时,必须附上这个ID。然后我们还需要在客户端和服务端中分别维护一个 【函数 <----> Call ID】的对应表。两者的表不一定需要完全相同,但是相同的函数对应的 Call ID 必须相同。当客户端需要进行远程调用时,他就查一下这个表,找出相应的 Call ID ,然后将他传入到服务端,服务端也通过查表来确定客户端需要调用的函数,然后执行相应函数的代码。
1.2 序列化和反序列化
客户端怎么把参数值传给远程的函数呢??在本地调用中,我们只需要把参数压入到栈中,然后让函数自己去栈萝莉读就行。但是在远程过程调用时,客户端根服务端是不同的进程,不能通过内存来传递参数。甚至有时候客户端和服务端使用的都不是同一种语言(比如服务端用C++,客户端使用 Java 或者 python)。这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。这个过程叫做序列化和反序列化。同理,从服务端返回的值夜需要通过序列化和反序列化的过程。
1.3 网络传输
远程调用往往用在网络中,客户端和服务端是通过网络连接的。所有的数据都需要通过网络传输,因此就需要有一个网络传输层。网络传输层需要把 Call ID 和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回给客户端。只要能完成这两者的,都可以用作为传输层使用。因此,他所使用的协议其实是不限的,只要可以完成传输就行了。尽管大部分RPC框架都使用TCP协议,但是其实UDP也是可以的,而gRPC干脆就使用HTTP2。
二、上述三个机制的解决方法
当我们解决了上述的三个问题,就可以实现RPC了,具体过程如下:
2.1 client 端可以解决的问题
- 将这个调用映射为 Call ID ,这里假设使用最简单的字符串当 Call ID 的方法
- 将 Call ID ,a 和 b序列化。可以直接将他们的值以二进制形式打包
- 将2中得到的数据包发送给 ServerAddr,这就需要使用网络传输层
- 等待服务器调用成功,那么就将结果反序列化,并赋值给total
2.2 server 端可以解决的问题
- 在本地维护一个 Call ID 到函数指针的映射 call_id_map,我们可以使用dict来完成
- 等待请求,包括多线程的并发处理能力
- 得到一个请求后,将其数据包反序列化,得到相应的函数指针
- 将 a 和 rb 反序列化后,在本地调用 add 函数,得到结果
- 将结果序列化后通过网络返回给 Client
在上面的整个流程中,其中:
- Call ID 映射可以直接使用函数字符串,也可以使用整数ID。映射表一般就是一个哈希表
- 序列化和反序列化可以自己写,也可以使用 protobuf 或者 FlatBuffers 之类的
- 网络传输库可以自己写 socket ,或者使用 asio,ZeroMQ、Netty 之类的
实际上真正的开发过程中,除了上面的基本功能之外,还需要更多的细节:网络错误,流量控制,超时和重传。
三、最后,如何将远程的这些过程写出本地函数调用的感觉??
下面,我们可以使用 go 语言来进行实现这个问题,因为在 go 语言中提供的包还是很全的。
我们分为服务端和客户端来进行编写代码:我们先来看一看服务端的代码:
package mainimport ("encoding/json""fmt""net/http""strconv"
)func main() {// http// 返回的格式化为:json{ "data"=3 }// 1. callID 的问题 r.URL.Path 2. 数据的传输协议 url 的参数传递协议 3. 网络传输协议 httphttp.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) {_ = r.ParseForm() // 解析参数fmt.Println("Path: ", r.URL.Path)a, _ := strconv.Atoi(r.Form["a"][0])b, _ := strconv.Atoi(r.Form["b"][0])w.Header().Set("Content-Type", "application/json; charset=utf-8")jData, _ := json.Marshal(map[string]interface{}{"data": a + b,})_, _ = w.Write(jData)})_ = http.ListenAndServe(":8080", nil)
}
客户端中的代码如下:
package mainimport ("encoding/json""fmt""github.com/kirinlabs/HttpRequest"
)type ResponseData struct {Data int `json:"data"`
}func add(a, b int) int {// 传输协议req := HttpRequest.NewRequest() // 创建一个requestres, _ := req.Get(fmt.Sprintf("http://127.0.0.1:8080/%s?a=%d&b=%d", "add", a, b))body, _ := res.Body()rspData := ResponseData{}_ = json.Unmarshal(body, &rspData)return rspData.Data
}func main() {// 实现 rpcadd(1, 2)
}
四、RPC开发的要素分析
4.1 RPC开发的四大要素
RPC技术在架构设计上有四个部分组成,分别是:客户端,客户端存根,服务端。服务端存根。下面,我们来一一介绍:
- 客户端:服务调用发起方,也称为服务消费者
- 客户端存根:该程序运行在客户端所在的计算机机器上,主要用来存储要调用的服务器的地址,另外,该程序还负责将客户端请求远端服务器程序的数据信息打包成数据包,通过网络发送给服务端Stub程序,其次,还要接受服务器Stub程序发送的调用结果数据包,并解析返回给客户端。
- 服务端:远端的计算机机器上运行的程序,其中有客户端要调用的方法
- 服务端存根:接收客户Stub程序通过网络发送的请求消息数据包,并调用服务端中真正的程序功能方法,完成功能调用,其次,将服务端执行调用的结果进行数据处理打包发送给客户端Stub程序。
了解完了RPC技术的组成结构,我们来看一下具体是如何实现客户端到服务端的调用的。实际上,如果我们想要在网络中的任意两台计算机上实现进程调用进程,需要解决很多问题,比如:
- 两台物理机器在网络中要建立稳定可靠的通信连接
- 两台服务器的通信协议的定义问题,即两台服务器上的程序如何识别对方的请求和返回结果。也就是说两台计算机必须能够识别对方发来的信息,并且能够识别出其中的请求含义和返回含义,然后才能进行处理,这其实就是通信协议要完成的工作。

在上图中,通过10步完成了RPC,下面我们来具体描述一下RPC每一步的调用过程:
- 客户端想要发起一个远程过程调用,首先要通过调用本地客户端Stub程序的方式调用想要使用的功能方法名;
- 客户端Stub程序接收到了客户端的功能调用请求,将客户端请求调用的方法名,携带的参数等信息做序列化,操作,并打包成数据包;
- 客户端Stub查找到远程服务器程序的IP地址,调用Socket通信协议,通过网络发送给服务端
- 服务端Stub程序接收到客户端发送的数据包信息,并通过约定好的协议将数据进行反序列化,得到请求的方法名和请求参数等信息
- 服务端Stub程序准备相关数据,调用本地的Server对应的功能方法进行,并存入相应的参数,进行业务处理
- 服务端程序根据已有的业务逻辑执行调用过程,等到业务执行结束之后,将执行结果返回给服务端Stub程序
- 服务端Stub程序将程序调用结果按照约定的协议进行序列化,并通过网络发送回给客户端Stub程序
- 客户端Stub接收到服务端Server发送返回的数据,对数据进行反序列化的操作,并将调用返回的数据传递给客户端请求发送者
- 客户端请求发起者得到调用结果,整个RPC调用过程结束
五、RPC需要使用到的术语
通过上文一系列的文字描述和讲解,我们已经了解了RPC的由来和整个RPC调用过程。我们可以看到RPC是一系列操作的集合,其中涉及了很多对于数据的操作,以及网络通信。因此,我们对RPC中涉及到的技术做一个总结和分析:
- 动态代理技术:上文中我们提到的Client Stub 和Server Stub 程序,在具体的编码和开发实践的过程中,都是使用动态代理技术自动生成的一段程序
- 序列化和反序列化:在RPC调用的过程中,我们可以看到数据需要在一台机器上传输到另一台机器上。在互联网中,所有的数据都是以字节的形式进行传输的。而我们在编程的过程中,往往都需要使用数据对象,因此想要在网络上将对象和相关变量进行传输,就需要对数据对象做序列化和反序列化的操作。
相关文章:
【gRPC】什么是RPC——介绍一下RPC
说起RPC,博主使用CPP手搓了一个RPC项目,RPC简单来说,就是远程过程调用:我们一般在本地传入数据进行执行函数,然后返回一个结果;当我们使用RPC之后,我们可以将函数的执行过程放到另外一个服务器上…...
谈谈你对AQS的理解
AQS 是多线程同步器,它是 JUC 包中多个组件的底层实现,如 Lock、CountDownLatch、Semaphore等都用到了AQS。 从本质上来说,AQS 提供了两种锁机制,分别是排它锁,和共享锁。 排它锁,就是存在多线程竞争同一…...
Bitcoin全节点搭建
1. wget https://bitcoincore.org/bin/bitcoin-core-0.20.1/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz 2.tar -xzvf bitcoin-0.20.1-x86_64-linux-gnu.tar.gz mv bitcoin-0.20.1 bitcoin 3.创建配置文件(bitcoin.conf) mkdir -p /btc_data mkdir ~/.b…...
【mysql进阶】4-6. InnoDB 磁盘文件
InnoDB 磁盘⽂件 1 InnoDB存储引擎包含哪些磁盘⽂件? 🔍 分析过程 ✅ 解答问题 InnoDB的磁盘⽂件主要是表空间⽂件和其他⽂件,表空间包括:系统表空间、独⽴表空间、通⽤表空间、临时表空间和撤销表空间;其他⽂件有重做…...
HexForge:一款用于扩展安全汇编和十六进制视图的IDA插件
关于HexForge HexForge是一款用于扩展安全汇编和十六进制视图的IDA插件,在该工具的帮助下,广大研究人员可以方便地直接从 IDA Pro 界面数据解码、解密或执行安全数据审计任务。 功能介绍 1、从 IDA 的反汇编或十六进制视图复制原始十六进制;…...
WORFBENCH:一个创新的评估基准,目的是全面测试大型语言模型在生成复杂工作流 方面的性能。
2024-10-10,由浙江大学和阿里巴巴集团联合创建的WORFBENCH,一个用于评估大型语言模型(LLMs)生成工作流能力的基准测试。它包含了一系列的测试和评估协议,用于量化和分析LLMs在处理复杂任务时分解问题和规划执行步骤的能力。WORFBE…...
SpringBoot 集成 Activiti 7 工作流引擎
一. 版本信息 IntelliJ IDEA 2023.3.6JDK 17Activiti 7 二. IDEA依赖插件安装 安装BPM流程图插件,如果IDEA的版本超过2020,则不支持actiBPM插件。我的IDEA是2023版本我装的是 Activiti BPMN visualizer 插件。 在Plugins 搜索 Activiti BPMN visualizer 安装创建…...
UVM初学篇 -(22)UVM field_automation 域的自动化机制
field_automation机制是域的自动化的机制,这个机制的最大的优点是可以对一些变量进行批量的处理,比如对象拷贝、克隆、打印之类的变量。 一、 成员变量的注册 使用field_automation机制首先要用uvm_field 系列宏完成变量的注册,类中的成员变…...
STL二分查找
本课主要介绍容器部分里面的二分查找函数。涉及的函数有 3 个,这 3 个函数的强两个输入参数都和迭代器有关,或者说参数是可以迭代的,而第三个参数则是你要查找的值。 1. binary_search binary_search 的返回结果是 bool 值,如果找…...
啤酒游戏—企业经营决策沙盘
感谢黄浦区文华学院的邀请,今年是为南房集团开展系统思考培训的第二年。我们现在为客户设计的一整年系统思考训练中,会将系统环路结构图与真实议题研讨作为前置内容,让大家在理解整体框架后,再体验麻省理工学院系统动力学著名的“…...
尚硅谷-react教程-求和案例-@redux-devtools/extension 开发者工具使用-笔记
## 7.求和案例_react-redux开发者工具的使用(1).npm install redux-devtools/extension(2).store中进行配置import { composeWithDevTools } from redux-devtools/extension;export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk))) src/redux/s…...
【动手学强化学习】part2-动态规划算法
阐述、总结【动手学强化学习】章节内容的学习情况,复现并理解代码。 文章目录 一、什么是动态规划?1.1概念1.2适用条件 二、算法示例2.1问题建模2.2策略迭代(policyiteration)算法2.2.1伪代码2.2.2完整代码2.2.3运行结果2.2.4代码…...
【python爬虫实战】爬取全年天气数据并做数据可视化分析!附源码
由于篇幅限制,无法展示完整代码,需要的朋友可在下方获取!100%免费。 一、主题式网络爬虫设计方案 1. 主题式网络爬虫名称:天气预报爬取数据与可视化数据 2. 主题式网络爬虫爬取的内容与数据特征分析: - 爬取内容&am…...
初识Linux · 动静态库(incomplete)
目录 前言: 静态库 动态库 前言: 继上文,我们从磁盘的理解,到了文件系统框架的基本搭建,再到软硬链接部分,我们开始逐渐理解了为什么运行程序需要./a.out了,这个前面的.是什么我们也知道了。…...
华为OD机试 - 匿名信(Java 2024 E卷 100分)
华为OD机试 2024E卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(E卷D卷A卷B卷C卷)》。 刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加…...
通过rancher2.7管理k8s1.24及1.24以上版本的k8s集群
目录 初始化实验环境 安装Rancher 登录Rancher平台 通过Rancher2.7管理已存在的k8s最新版集群 文档中的YAML文件配置直接复制粘贴可能存在格式错误,故实验中所需要的YAML文件以及本地包均打包至网盘. 链接:https://pan.baidu.com/s/1oYX4eGoBtW_R-7i…...
text-align的属性justify
text-align常用的属性是left、center、right,具体的可参考css解释,今天重点记录的对象是justify justify 可以使文本的两端都对齐在两端对齐文本中,文本行的左右两端都放在父元素的内边界上。然后,调整单词和字母间的间隔&#x…...
使用python自制桌面宠物,好玩!——枫原万叶桌宠,可以直接打包成exe去跟朋友炫耀。。。
大家好,我是小黄。 今天我们使用python实现一个桌面宠物。只需要gif动态图片就行。超级简单容易上手。 #完整源代码可在下方图片免费获取 一:下载相关的库文件。 我们本次使用到的库文件为:tkinter和pyautogui 下载命令: pip…...
使用 ASP.NET Core 8.0 创建最小 API
构建最小 API,以创建具有最小依赖项的 HTTP API。 它们非常适合需要在 ASP.NET Core 中仅包括最少文件、功能和依赖项的微服务和应用。 本教程介绍使用 ASP.NET Core 生成最小 API 的基础知识。 在 ASP.NET Core 中创建 API 的另一种方法是使用控制器。 有关在最小 …...
气候服务平台ClimateSERV2.0简介(python)
1 简介 ClimateSERV 2.0允许开发从业者、科学家/研究人员和政府决策者可视化和下载历史降雨数据、植被状况数据以及 180 天的降雨和温度预报,以增进对农业和水资源供应相关问题的理解并做出改进的决策。 这些数据可以通过 Web 应用程序直接访问,也可以…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
OpenGL-什么是软OpenGL/软渲染/软光栅?
软OpenGL(Software OpenGL)或者软渲染指完全通过CPU模拟实现的OpenGL渲染方式(包括几何处理、光栅化、着色等),不依赖GPU硬件加速。这种模式通常性能较低,但兼容性极强,常用于不支持硬件加速…...
Springboot多数据源配置实践
Springboot多数据源配置实践 基本配置文件数据库配置Mapper包Model包Service包中业务代码Mapper XML文件在某些复杂的业务场景中,我们可能需要使用多个数据库来存储和管理不同类型的数据,而不是仅仅依赖于单一数据库。本技术文档将详细介绍如何在 Spring Boot 项目中进行多数…...
设计模式域——软件设计模式全集
摘要 软件设计模式是软件工程领域中经过验证的、可复用的解决方案,旨在解决常见的软件设计问题。它们是软件开发经验的总结,能够帮助开发人员在设计阶段快速找到合适的解决方案,提高代码的可维护性、可扩展性和可复用性。设计模式主要分为三…...
