RPC 原理详解

news2024/11/26 16:25:19

文章目录

    • 什么是 RPC
    • RPC 基本原理
      • RPC核心功能
      • 服务寻址
      • 数据编解码
      • 网络传输
      • 一次RPC的调用过程
    • 实践
      • 基于HTTP协议的RPC
      • 基于TCP协议的RPC

什么是 RPC

RPC(Remote Procedure Call),即远程过程调用,它允许像调用本地服务一样调用远程服务。是一种服务器-客户端(Client/Server)模式。

  • 远程:指的是需要经过网络的,而不是应用内部、机器内部进行的。
  • 过程:也就是方法。

那“远程过程调用”,就是:可以跨过一段网络,调用另外一个网络节点上的方法。以上就是对远程过程调用的简单理解。

RPC 调用分以下两种:

  1. 同步调用:客户方等待调用执行完成并返回结果。
  2. 异步调用:客户方调用后不用等待执行结果返回,但依然可以通过回调通知等方式获取返回结果。 若客户方不关心调用返回结果,则变成单向异步调用,单向调用不用返回结果。

异步和同步的区分在于是否等待服务端执行完成并返回结果。

RPC 基本原理

RPC核心功能

知道什么是RPC以后就会发现,RPC需要解决一些问题:

  1. 既然是远程调用,那么客户端如何知道服务端的地址?
  2. 如果客户端和服务端使用的是不同语言写的程序,那么参数该如何表达和解析?
  3. 如何进行网络传输?

这三个问题的解决方案也是RPC的核心功能:服务寻址、数据编解码和网络传输。

服务寻址

如果是本地调用,被调用的方法在同一个进程内,操作系统或者是虚拟机可以去地址空间去找;但是在远程调用中,这是行不通的,因为两个进程的地址空间是完全不一样的,肯定也无法知道远端的进程在那。

如果要想实现远程调用,我们需要对服务消费者和服务提供者两者进行约束:在远程过程调用中所有的函数都必须有一个 ID,这个 ID 在整套系统中是唯一存在确定的。服务消费者在做远程过程调用时,发送的消息体中必须要携带这个 ID。服务消费者和服务提供者分别维护一个函数和 ID 的对应表。当服务消费者需要进行远程调用时,它就查一下这个表,找出对应的 ID,然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应函数的代码就行。

服务寻址的实现方式有很多种,常见的是:服务注册中心。要调用服务,首先你需要一个服务注册中心去查询对方服务都有哪些实例,然后根据负载均衡策略择优选一。

服务注册
  1. 从服务提供者的角度看:当提供者服务启动时,需要自动向注册中心注册服务;当提供者服务停止时,需要向注册中心注销服务;提供者需要定时向注册中心发送心跳。如果一段时间未收到来自提供者的心跳后,注册中心会判定提供者已经停止服务,并从注册中心下架对应的服务。
  2. 从调用者的角度看:调用者启动时订阅注册中心的消息并从注册中心获取提供者的地址;当有提供者上线或者下线时,注册中心会告知到调用者;调用者下线时,取消订阅。

数据编解码

对计算机网络稍微有一点熟悉的同学都知道,数据在网络中传输都是二进制的:01010101010101010,类似这种,只有二进制数据才能在网络间传。选择好的序列化协议特别重要,一个好的序列化协议能减少序列化数据带来的性能损耗。常见的RPC序列化协议如下:

  • XML(Extensible Markup Language)是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点。狭义web service就是基于SOAP消息传递协议(一个基于XML的可扩展消息信封格式)来进行数据交换的。

  • JSON(Javascript Object Notation)起源于弱类型语言Javascript, 是采用"Attribute-value"的方式来描述对象协议。与XML相比,其协议比较简单,解析速度比较快。

  • Protocol Buffers 是google提供的一个开源序列化框架,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。同 XML 相比, Protobuf 的主要优点在于性能高。它以高效的二进制方式存储,比 XML 小 3 到 10 倍,快 20 到 100 倍。

以上每种协议都有其优点和适用场景,需要根据具体的需求和环境来选择合适的协议。

网络传输

提起网络传输大家脑海里肯定马上就能想到 TCP/IP四层模型、OSI 七层模型,那通常 RPC 会选择那一层作为传输协议呢?

在回答这个问题前,先来看下 RPC 需要网络传输实现什么样的功能。客户端的数据经过序列化后,就需要通过网络传输到服务端。网络传输层需要把前面说的函数 ID 和序列化后的参数字节流传给服务端,服务端处理完然后再把序列化后的调用结果传回客户端。

原则上只要能实现上面这个功能的都可以作为传输层来使用,具体协议没有限制。我们先来看下 TCP 协议,TCP 连接可以是按需连接,需要调用的时候就先建立连接,调用结束后就立马断掉,也可以是长连接,客户端和服务器建立起连接之后保持长期持有,不管此时有无数据包的发送,可以配合心跳检测机制定期检测建立的连接是否存活有效。

由此可见 TCP 的性能确实很好,因此市面上大部分 RPC 框架都使用 TCP 协议,但也有少部分框架使用其他协议,比如 gRPC 用的是 HTTP2 来实现。

一次RPC的调用过程

忽略服务端向注册中心注册服务的流程,下面是客户端和服务端之间进行一次RPC调用的完整过程。

image-20231102193528606
  1. 客户端(Client)通过本地调用的方式调用服务(以接口方式调用);
  2. 客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息进行组装序列化成能够进行网络传输的消息体(将消息体对象序列化为二进制流);
  3. 客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端(通过sockets发送消息);
  4. 服务端存根(Server Stub)收到消息后进行反序列化操作,即解码(将二进制流反序列化为消息对象);
  5. 服务端存根(Server Stub)通过解码结果调用本地的服务进行相关处理;
  6. 服务端(Server)将处理结果返回给服务端存根;
  7. 服务端存根(Server Stub)序列化处理结果(将结果消息对象序列化为二进制流);
  8. 服务端存根(Server Stub)将序列化结果通过网络发送至客户端(通过sockets发送消息);
  9. 客户端存根(Server Stub)接收到消息,进行反序列化解码(将结果二进制流反序列化为消息对象);
  10. 客户端得到最终的结果。

这里面有一个词语:存根(Stub)。这里存根的作用我认为和Linux内核里面的库打桩机制有点类似。在Linux中,一个"桩"(stub)就是一个程序或函数的临时替代品,"桩"可以模拟出类似于真实的程序或函数的行为。所以,在RPC中,客户端存根和服务器存根的作用是隐藏RPC底层机制的复杂性,让开发者可以像调用本地函数一样调用远程函数。

实践

基于HTTP协议的RPC

服务端代码:

type Args struct {
	A, B int
}

type Compute int

func (c *Compute) Add(args *Args, reply *int) error {
	*reply = args.A + args.B
	return nil
}

func main() {
	compute := new(Compute)
	rpc.HandleHTTP() // 注册 HTTP 路由
    // 注册 RPC 服务
	if err := rpc.Register(compute); err != nil {
		log.Fatal("Register error:", err)
	}

	listen, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal("Listen error:", err)
	}
	if err = http.Serve(listen, nil); err != nil {
		log.Fatal("Serve error:", err)
	}
}

rpc库对注册的方法有一定的限制,方法必须满足签名func (t *T) MethodName(argType T1, replyType *T2) error{}

  1. 方法名必需是可导出的。
  2. 方法接收两个参数,这两个参数都是可导出的,且第二个参数必需为指针类型。
  3. 方法必需返回一个error类型的参数。

客户端代码:

type Args struct {
	A, B int
}

func main() {
	client, err := rpc.DialHTTP("tcp", "localhost:8080")
	if err != nil {
		log.Fatal("dialing:", err)
	}

	args := &Args{3, 5}
	// 同步调用
	var reply1 int
	if err = client.Call("Compute.Add", args, &reply1); err != nil {
		log.Fatal("Compute error:", err)
	}
	fmt.Printf("同步调用的sum: %d\n", reply1)

	// 异步调用
	var reply2 int
	divCall := client.Go("Compute.Add", args, &reply2, nil)
	_ = <-divCall.Done // 接收调用结果
	fmt.Printf("异步调用的sum: %d\n", reply2)
}

运行结果如下:

PS D:\GolandProjects\RPC\client> go run .\client.go
同步调用的sum: 8
异步调用的sum: 8

基于TCP协议的RPC

服务端代码:

type Args struct {
	A, B int
}

type Compute int

func (c *Compute) Add(args *Args, reply *int) error {
	*reply = args.A + args.B
	return nil
}

func main() {
	compute := new(Compute)
	if err := rpc.Register(compute); err != nil {
		log.Fatal("Register error:", err)
	}

	listen, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal("Listen error:", err)
	}
	rpc.Accept(listen)
}

客户端代码:

type Args struct {
	A, B int
}

func main() {
	client, err := rpc.Dial("tcp", "localhost:8080")
	if err != nil {
		log.Fatal("dialing:", err)
	}

	args := &Args{6, 8}
	// 同步调用
	var reply1 int
	if err = client.Call("Compute.Add", args, &reply1); err != nil {
		log.Fatal("Compute error:", err)
	}
	fmt.Printf("同步调用的sum: %d\n", reply1)

	// 异步调用
	var reply2 int
	divCall := client.Go("Compute.Add", args, &reply2, nil)
	_ = <-divCall.Done // 接收调用结果
	fmt.Printf("异步调用的sum: %d\n", reply2)
}

运行结果:

PS D:\GolandProjects\RPC\client> go run .\client.go
同步调用的sum: 14
异步调用的sum: 14

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

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

相关文章

思路视野杂志思路视野杂志社思路视野编辑部2023年第24期目录

公共文化 公共图书馆文旅融合实践与模式思考 白雪1-3 公共图书馆管理与服务创新路径分析 陈静4-6 提升办公室文书档案管理工作的实践探讨 黄强7-9 《思路视野》投稿邮箱&#xff1a;cn7kantougao163.com(注明投稿“《思路视野》”) 崔编辑Q Q &#xff1a;296078736 微信号&am…

STL-set和map

目录 一、pair和make_pair 1. pair 2. make_pair 二、set &#xff08;一&#xff09;set的模板参数列表 &#xff08;二&#xff09;set的构造 &#xff08;三&#xff09;set的插入 1. 测试1 2. 测试2 &#xff08;四&#xff09;low_bound和upper_bound&#xff…

mediapipe流水线分析 一

object detection Graph 以目标检测为例分析mediapip流水线处理机制 一 流水线上游输入处理 1 Calculator 算子 它是在MediaPipe框架中用于创建插件/算子 机制的基础 在MediaPipe中&#xff0c;插件是一种可扩展的计算模块&#xff0c;可以用于实现各种不同的计算功能。calc…

路由器基础(九):防火墙基础

防火墙 (Fire Wall) 是网络关联的重要设备&#xff0c;用于控制网络之间的通信。外部网络用户的访问必须先经过安全策略过滤&#xff0c;而内部网络用户对外部网络的访问则无须过滤。现在的防火墙还具有隔离网络、提供代理服务、流量控制等功能。 一、三种防火墙技术 常见的…

努努之歌:英雄联盟外传 SongOfNunu(单人冒险游戏) 免安装中文版下载

《努努之歌&#xff1a;英雄联盟外传》是一款由Tequila Works开发的单人冒险游戏。置身于弗雷尔卓德的未知之地&#xff0c;探索扣人心弦的故事&#xff0c;一步步揭开努努和威朗普的过去&#xff0c;了解背后的真相。 游戏介绍 建立深厚友谊 扮演小男孩努努&#xff0c;在好朋…

WebGL:基础练习 / 简单学习 / demo / canvas3D

一、前置内容 canvas&#xff1a;理解canvas / 基础使用 / 实用demo-CSDN博客 WebGL&#xff1a;开始学习 / 理解 WebGL / WebGL 需要掌握哪些知识 / 应用领域 / 前端值得学WebGL吗_webgl培训-CSDN博客 二、在线运行HTML 用来运行WebGL代码&#xff0c;粘贴--运行&#xff…

Flutter 06 动画

一、动画基本原理以及Flutter动画简介 1、动画原理&#xff1a; 在任何系统的Ul框架中&#xff0c;动画实现的原理都是相同的&#xff0c;即&#xff1a;在一段时间内&#xff0c;快速地多次改变Ul外观&#xff1b;由于人眼会产生视觉暂留&#xff0c;所以最终看到的就是一个…

算法:弗洛里德算法Floyd

弗洛里德算法 简介 英文名Floyd 作用&#xff1a;寻找图中任意两点之间的最短路径 数据结构&#xff1a;邻接矩阵&#xff08;二维数组&#xff09; 思想 如果说从start到end之间转个弯能使得路径变短,那么就记录更短的路径&#xff0c; 对所有弯都试一下没到最后取最短的&am…

Zabbix监控联想服务器的配置方法

简介 图片 随着科技的发展&#xff0c;对于数据的敏感和安全大部分取决于对硬件性能、故障预判的监测&#xff0c;由此可见实时监测保障硬件的安全很重要&#xff0c;从而衍生了很多对硬件的监测软件&#xff0c;Zabbix就一个不错的选择。开源 开源 开源&#xff01; zabbix是…

SpringCloud(八) Gateway网关路由详解

目录 一, Gateway服务网关 1.1 为什么需要网关 1.2 网关的实现方式 1.3 Gateway快速入门 1. 创建gateway服务,引入依赖 2. 编写启动类 3. 编写基础配置和路由规则 4. 重启测试 5. 网关路由的流程图 6. 总结 二, Gateway断言工厂 三, 过滤器工厂 3.1 路由过滤器…

【Mysql】Mysql中表连接的原理

连接简介 在实际工作中&#xff0c;我们需要查询的数据很可能不是放在一张表中&#xff0c;而是需要同时从多张表中获取。下面我们以简单的两张表为例来进行说明。 连接的本质 为方便测试说明&#xff0c;&#xff0c;先创建两个简单的表并给它们填充一点数据&#xff1a; …

【Truffle】三、可视化测试报告的生成

在truffle中&#xff0c;我们可以引入第三方插件&#xff0c;对truffle的测试进行更好的提升&#xff0c;这里介绍两个插件&#xff0c;分别是mocha-junit-reporter和mochawesome两个插件。 一、mocha-junit-reporter插件 mocha-junit-reporter是一个用于Truffle测试框架的插件…

随机森林算法的加速:OpenMP方法

使用OpenMP加速随机森林 前言使用OpenMP对RF训练的加速代码加速效果 对特征重要性评估的加速代码 加速效果附录Windows中使用OpenMPLinux中使用OpenMP 项目主页&#xff1a;randomforest C implementation of random forests classification, regression, proximity and variab…

【德哥说库系列】-Oracle 19C PDB创建大全

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

双绞线(寻线仪,测线仪),光纤测试工具(红光笔,OTDR,光功率计)

网络测试方式&#xff1a; 根据测试中是否向被测网络注入测试流量&#xff0c;可以将网络测试方法分为主动测试和被动测试。 主动测试&#xff1a;利用测试工具有目的地主动问被测网络注入测试流量&#xff0c;根据测试流量的传送情况分析网络技术参数。优点是具备良好的灵活…

读程序员的制胜技笔记03_有用的反模式(上)

1. 教条 1.1. 一成不变的法则 1.2. 这些东西会遮蔽我们的双眼&#xff0c;你坚持相信的时间越久&#xff0c;你被遮蔽双眼的程度也就越深 2. 质疑所有教给你的东西 2.1. 它们有多么有用 2.2. 使用它们的理由 2.3. 使用它们的好处 2.4. 使用它们的代价 3. 反模式 3.1. …

13、Kubernetes核心技术 - Ingress

目录 一、概述 二、Ingress工作原理 三、Ingress 使用 3.1)、Ingress-http方式 3.1.1)、创建Ingress Controller和对应的Service 3.1.2)、创建tomcat的Pod和Service 3.1.3)、创建nginx的Pod和Service 3.1.4)、创建ingress http代理 3.1.5)、配置本地host文件 3.1.6)、…

基于STM32设计的室内环境监测系统(华为云IOT)_2023

一、设计需求 基于STM32+华为云物联网平台设计一个室内环境监测系统,以STM32系列单片机为主控器件,采集室内温湿度、空气质量、光照强度等环境参数,将采集的数据结果在本地通过LCD屏幕显示,同时上传到华为云平台并将上传的数据在Android移动端能够实时显示、查看。 【1…

更新版PHP神算网八字算命星座解梦周易占卜程序源码/PC+H5移动端整站适配/PHP源码带手机版

源码简介&#xff1a; 这个是更新版PHP神算网八字算命星座解梦周易占卜程序源码&#xff0c;能够在PCH5移动端整站适配。作为H5付费算命PHP源码&#xff0c;八字算命网站源码&#xff0c;功能很多强大实用。 2023.3 更新记录&#xff1a; 1、更新了23年属相信息&#xff1b;…

12 pinctrl 和 gpio 子系统

一、pinctrl 子系统 1. 什么是 pinctrl 子系统&#xff1f; 首先回顾一下如何初始化 LED 所使用的 GPIO&#xff1a; ①、修改设备树&#xff0c;添加相应的节点&#xff0c;节点里面重点是设置 reg 属性&#xff0c; reg 属性包括了 GPIO相关寄存器。 ②、获取 reg 属性中 …