【gRPC】什么是RPC——介绍一下RPC

news2025/4/9 5:47:42

       说起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 main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
)

func main() {
	// http
	// 返回的格式化为:json{ "data"=3 }
	// 1. callID 的问题 r.URL.Path  2. 数据的传输协议 url 的参数传递协议  3. 网络传输协议 http
	http.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 main

import (
	"encoding/json"
	"fmt"
	"github.com/kirinlabs/HttpRequest"
)

type ResponseData struct {
	Data int `json:"data"`
}

func add(a, b int) int {
	// 传输协议
	req := HttpRequest.NewRequest() // 创建一个request
	res, _ := 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() {
	// 实现 rpc
	add(1, 2)
}

四、RPC开发的要素分析

4.1 RPC开发的四大要素

       RPC技术在架构设计上有四个部分组成,分别是:客户端,客户端存根,服务端。服务端存根。下面,我们来一一介绍:

  • 客户端:服务调用发起方,也称为服务消费者
  • 客户端存根:该程序运行在客户端所在的计算机机器上,主要用来存储要调用的服务器的地址,另外,该程序还负责将客户端请求远端服务器程序的数据信息打包成数据包,通过网络发送给服务端Stub程序,其次,还要接受服务器Stub程序发送的调用结果数据包,并解析返回给客户端。
  • 服务端:远端的计算机机器上运行的程序,其中有客户端要调用的方法
  • 服务端存根:接收客户Stub程序通过网络发送的请求消息数据包,并调用服务端中真正的程序功能方法,完成功能调用,其次,将服务端执行调用的结果进行数据处理打包发送给客户端Stub程序。

       了解完了RPC技术的组成结构,我们来看一下具体是如何实现客户端到服务端的调用的。实际上,如果我们想要在网络中的任意两台计算机上实现进程调用进程,需要解决很多问题,比如:

  • 两台物理机器在网络中要建立稳定可靠的通信连接
  • 两台服务器的通信协议的定义问题,即两台服务器上的程序如何识别对方的请求和返回结果。也就是说两台计算机必须能够识别对方发来的信息,并且能够识别出其中的请求含义和返回含义,然后才能进行处理,这其实就是通信协议要完成的工作。

在上图中,通过10步完成了RPC,下面我们来具体描述一下RPC每一步的调用过程:

  1. 客户端想要发起一个远程过程调用,首先要通过调用本地客户端Stub程序的方式调用想要使用的功能方法名;
  2. 客户端Stub程序接收到了客户端的功能调用请求,将客户端请求调用的方法名,携带的参数等信息做序列化,操作,并打包成数据包;
  3. 客户端Stub查找到远程服务器程序的IP地址,调用Socket通信协议,通过网络发送给服务端
  4. 服务端Stub程序接收到客户端发送的数据包信息,并通过约定好的协议将数据进行反序列化,得到请求的方法名和请求参数等信息
  5. 服务端Stub程序准备相关数据,调用本地的Server对应的功能方法进行,并存入相应的参数,进行业务处理
  6. 服务端程序根据已有的业务逻辑执行调用过程,等到业务执行结束之后,将执行结果返回给服务端Stub程序
  7. 服务端Stub程序将程序调用结果按照约定的协议进行序列化,并通过网络发送回给客户端Stub程序
  8. 客户端Stub接收到服务端Server发送返回的数据,对数据进行反序列化的操作,并将调用返回的数据传递给客户端请求发送者
  9. 客户端请求发起者得到调用结果,整个RPC调用过程结束

五、RPC需要使用到的术语

       通过上文一系列的文字描述和讲解,我们已经了解了RPC的由来和整个RPC调用过程。我们可以看到RPC是一系列操作的集合,其中涉及了很多对于数据的操作,以及网络通信。因此,我们对RPC中涉及到的技术做一个总结和分析:

  • 动态代理技术:上文中我们提到的Client Stub 和Server Stub 程序,在具体的编码和开发实践的过程中,都是使用动态代理技术自动生成的一段程序
  • 序列化和反序列化:在RPC调用的过程中,我们可以看到数据需要在一台机器上传输到另一台机器上。在互联网中,所有的数据都是以字节的形式进行传输的。而我们在编程的过程中,往往都需要使用数据对象,因此想要在网络上将对象和相关变量进行传输,就需要对数据对象做序列化和反序列化的操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2225344.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go语言中的Scanf()输入函数

Scanf() 第一种情况 package mainimport "fmt"func main() {var a intfor {fmt.Println("请输入一个整数:")fmt.Scanf("%d", &a)fmt.Println("----------------", a)} }运行结果&#xff1a; 解释&#xff1a; 出现这种现象是因…

Docker下载途径

Docker不是Linux自带的&#xff0c;需要我们自己安装 官网&#xff1a;https://www.docker.com/ 安装步骤&#xff1a;https://docs.docker.com/engine/install/centos/ Docker Hub官网(镜像仓库)&#xff1a;https://hub.docker.com/ 在线安装docker 先卸载旧的docker s…

C/C++小宇宙代码

系列目录 序号直达链接1C/C爱心代码2C/C跳动的爱心3C/C李峋同款跳动的爱心代码4C/C满屏飘字表白代码5C/C大雪纷飞代码6C/C烟花代码7C/C黑客帝国同款字母雨8C/C樱花树代码9C/C奥特曼代码10C/C圣诞树代码11C/C俄罗斯方块12C/C贪吃蛇13C/C小宇宙代码 目录 系列目录 写在前面 …

LVGL显示图片2——显示GIF图片,图片尺寸注意,图片太大无法显示

1进入图像转换网页中 https://lvgl.io/tools/imageconverter 2选择图片 3设置生成的格式 4点击生成 5复制文件&#xff0c;配置环境 6编写代码&#xff08;&#xff09; //显示一张动图 void demo_gif(){lv_obj_t* screenlv_scr_act();//声名对象LV_IMG_DECLARE(GIF_1);//创…

【数据集】NCEP辐射数据-用于计算漫射天窗比(diffuse skylight ration)

【数据集】NCEP辐射数据-用于计算漫射天窗比(diffuse skylight ration) 漫射天窗比(diffuse skylight ration)地表反射率计算漫射天窗比计算NCEP辐射数据数据下载参考漫射天窗比(diffuse skylight ration) 基于NCEP辐射数据利用Python代码计算漫射天窗比(diffuse skyli…

【实用知识】Spring Boot 优雅捕捉异常的几种姿势

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

ATom:加州理工学院化学电离质谱仪(CIT-CIMS)的现场数据,V2版

目录 简介 摘要 代码 引用 网址推荐 知识星球 机器学习 ATom: In Situ Data from Caltech Chemical Ionization Mass Spectrometer (CIT-CIMS), V2 ATom&#xff1a;加州理工学院化学电离质谱仪&#xff08;CIT-CIMS&#xff09;的现场数据&#xff0c;V2版 简介 该数…

Centos7.9安装MySQL(二进制)

安装包 https://downloads.mysql.com/archives/community/ mysql-8.0.39-linux-glibc2.17-x86_64.tar.xz 1.卸载MariaDB 查看 rpm -qa|grep mariadb卸载 可能名称不一样&#xff0c;记得替换 rpm -e --nodeps mariadb-libs-5.5.68-1.el7.x86_64rpm -qa|grep mariadb 执行…

leetcode228:汇总取件

步骤1&#xff1a;定义题目的计算问题性质 题目要求从一个无重复元素且有序的整数数组 nums 中&#xff0c;找出所有恰好覆盖数组中所有数字的最小区间范围列表。这意味着每个数字都必须被某个区间包含&#xff0c;且没有多余的数字在区间内。 输入&#xff1a;一个有序整数数…

Github 2024-10-28 开源项目周报 Top15

根据Github Trendings的统计,本周(2024-10-28统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目6JavaScript项目4TypeScript项目4Jupyter Notebook项目2C++项目1C#项目1Svelte: 一种新的Web应用程序构建方法 创建周期:2899 天开发…

软件分享丨ImTip输入状态

ImTip软件介绍 ImTip是一款实用的输入法状态跟踪提示工具&#xff0c;官网为https://imtip.aardio.com。 它具有诸多优势&#xff0c;体积小巧仅696KB且免费开源&#xff0c;是单文件绿色软件&#xff0c;无外部依赖&#xff0c;能在多种流行桌面操作系统上流畅运行。其核心…

软硬件开发面试问题大汇总篇——针对非常规八股问题的提问与应答(代码规范与生态管理)

软硬件开发&#xff0c;对于编码规范、生态管理等等综合问题的考察尤为重要。 阐述下环形缓冲区的用途 环形缓冲区&#xff08;Ring Buffer&#xff09;是一种固定大小的数据结构&#xff0c;常用于实现数据的流式传输或临时存储。在环形缓冲区中&#xff0c;当到达缓冲区的末尾…

react18中的合成事件与浏览器中的原生事件

React 通过将事件 normalize 以让他们在不同浏览器中拥有一致的属性。 合成事件 SyntheticEvent 实例将被传递给你的事件处理函数&#xff0c;它是浏览器的原生事件的跨浏览器包装器。除兼容所有浏览器外&#xff0c;它还拥有和浏览器原生事件相同的接口&#xff0c;包括 stopP…

Atlas800昇腾服务器(型号:3000)—SwinTransformer等NPU推理【图像分类】(九)

服务器配置如下&#xff1a; CPU/NPU&#xff1a;鲲鹏 CPU&#xff08;ARM64&#xff09;A300I pro推理卡 系统&#xff1a;Kylin V10 SP1【下载链接】【安装链接】 驱动与固件版本版本&#xff1a; Ascend-hdk-310p-npu-driver_23.0.1_linux-aarch64.run【下载链接】 Ascend-…

华为配置 之 STP

目录 简介&#xff1a; STP&#xff1a; RSTP: 如何改变根网桥&#xff1a; &#xff08;1&#xff09;改变优先级&#xff1a; &#xff08;2&#xff09;改变root: 各端口的状态&#xff1a; 总结&#xff1a; 简介&#xff1a; STP&#xff08;Spanning Tree Protoco…

【数据分析】Power BI的使用教程

目录 1 Power BI架构1.1 Power BI Desktop1.2 Power BI服务1.3 Power BI移动版 2 Power Query2.1 Power Query编辑器2.2 Power Query的优点2.3 获取数据2.4 数据清洗的常用操作2.4.1 提升标题2.4.2 更改数据类型2.4.3 删除错误/空值2.4.4 删除重复项2.4.5 填充2.4.6 合并列2.4.…

JAVA面试八股文(五)

#1024程序员节&#xff5c;征文# 在1024程序员节这个特别的日子里&#xff0c;首先&#xff0c;我想对每一位程序员表示最诚挚的祝贺&#xff01;祝愿大家在未来的日子里&#xff0c;能够继续热爱编程、追求卓越&#xff0c;携手共创更美好的科技未来&#xff01;让我们共同庆祝…

基于Django+python的酒店客房入侵检测系统设计与实现

项目运行 需要先安装Python的相关依赖&#xff1a;pymysql&#xff0c;Django3.2.8&#xff0c;pillow 使用pip install 安装 第一步&#xff1a;创建数据库 第二步&#xff1a;执行SQL语句&#xff0c;.sql文件&#xff0c;运行该文件中的SQL语句 第三步&#xff1a;修改源…

Luma API 的使用

Luma 是一个专注于高质量视频生成的平台&#xff0c;用户只需上传素材&#xff0c;即可根据不同的风格和效果自动生成高质量的视频。该 AI 视频生成器是由来自知名科技公司的团队开发&#xff0c;旨在让每个人在不使用复杂编辑工具的情况下&#xff0c;轻松制作优秀的视频。 然…

docker sameersbn/bind dns服务器

1. 安装 #下载docker 镜像 docker pull sameersbn/bind#运行 53端口若被占用会启动失败 docker run --name dns -d --restartalways \ --publish 53:53/tcp \ --publish 53:53/udp \ --publish 10000:10000/tcp \ -v /etc/localtime:/etc/localtime \ -v /data/bind/:/data \…