GoLong的学习之路,进阶,微服务之原理,RPC

news2024/11/17 17:24:50

其实我早就很想写这篇文章了,RPC是一切现代计算机应用中非常重要的思想。也是微服务和分布式的总体设计思想。只能说是非常中要,远的不说,就说进的这个是面试必问的。不管用的上不,但是就是非常重要。

文章目录

  • RPC的原理
    • 本地调用
    • RPC调用
      • server服务
      • 客户端来请求上述HTTP服务
      • `RPC原理`
  • 如何做到透明化(封装)远程服务调用
    • 对消息进行编码和解码
    • 序列化

RPC的原理

RPC(Remote Procedure Call Protocol)远程过程调用协议。一个通俗的描述是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。

一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。

这里我做一个解释:是不是有人会将这个与TCP等传输协议放在一起比较,而我想说这么比较是对的。这里献上一篇文章–tcp、http、rpc和grpc总结,但是要注意,这其中的区别

不过这里我做一个强调,这篇文章必须将这些协议弄清楚了,才能看,他不是雪中送碳,而是锦上添花。

RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统。

首先与RPC(远程调用)相对应的是本地调用。

本地调用

先看一个go文件

package main

import "fmt"

func add(x, y int)int{
	return x + y
}

func main(){
	// 调用本地函数add
	a := 10
	b := 20
	ret := add(a, x)
	fmt.Println(ret)
}

在程序中本地调用add函数的执行流程,可以理解为以下四个步骤。

  1. 将变量 a b 的值分别压入堆栈上
  2. 执行 add 函数,从堆栈中获取 ab 的值,并将它们分配给x y
  3. 计算 x + y 的值并将其保存到堆栈中
  4. 退出 add 函数并将x + y的值赋给 ret

在这里插入图片描述

定义add函数的代码和调用add函数的代码共享同一个内存空间,所以调用能够正常执行

但是我们无法直接在另一个程序——中调用add函数,因为它们是两个程序——内存空间是相互隔离的。(两个进程不共享内存)

(两个程序可能在一个主机上,也可能在不同的主机上)

在这里插入图片描述

所以RPC就诞生了

RPC就是为了解决类似远程、跨内存空间、的函数或者方法调用的

RPC调用

为什么要用RPC

如果我们开发简单的单一应用,逻辑简单、用户不多、流量不大,那我们用不着。

当我们的系统访问量增大、业务增多时,我们会发现一台单机运行此系统已经无法承受。此时,我们可以将业务拆分成几个互不关联的应用,分别部署在各自机器上,以划清逻辑并减小压力。

此时,我们也可以不需要RPC,因为应用之间是互不关联的。

当我们的业务越来越多、应用也越来越多时,自然的,我们会发现有些功能已经不能简单划分开来或者划分不出来。

此时,可以将公共业务逻辑抽离出来,将之组成独立的服务Service应用 。而原有的、新增的应用都可以与那些独立的Service应用 交互,以此来完成完整的业务功能。

所以此时,我们急需一种高效的应用程序之间的通讯手段来完成这种需求.

描述的场景也是服务化 、微服务和分布式系统架构的基础场景。

要实现RPC就需要解决以下三个问题:

  1. 如何确定要执行的函数?

    • 在本地调用中,函数主体通过函数指针函数指定,然后调用 add 函数,编译器通过函数指针函数自动确定 add 函数在内存中的位置。
    • RPC 中,调用不能通过函数指针完成,因为它们的内存地址可能完全不同。因此,调用方和被调用方都需要维护一个{ function <-> ID }映射表,以确保调用正确的函数
  2. 如何表达参数?

    • 本地过程调用中传递的参数是通过堆栈内存结构实现的,但 RPC 不能直接使用内存传递参数,因此参数或返回值需要在传输期间序列化并转换成字节流,反之亦然。
  3. 如何进行网络传输?

    • 函数的调用方和被调用方通常是通过网络连接的,也就是说,function ID序列化字节流需要通过网络传输,因此,只要能够完成传输,调用方和被调用方就不受某个网络协议的限制。.
    • 例如,一些 RPC 框架使用 TCP 协议,一些使用 HTTP

以往实现跨服务调用的时候,我们会采用RESTful API的方式,被调用方会对外提供一个HTTP接口,调用方按要求发起HTTP请求并接收API接口返回的响应数据。

server服务

// server/main.go

package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
)

type addParam struct {
	X int `json:"x"`
	Y int `json:"y"`
}

type addResult struct {
	Code int `json:"code"`
	Data int `json:"data"`
}

func add(x, y int) int {
	return x + y
}

func addHandler(w http.ResponseWriter, r *http.Request) {
	// 解析参数
	b, _ := ioutil.ReadAll(r.Body)
	var param addParam
	json.Unmarshal(b, &param)
	// 业务逻辑
	ret := add(param.X, param.Y)
	// 返回响应
	respBytes , _ := json.Marshal(addResult{Code: 0, Data: ret})
	w.Write(respBytes)
}

func main() {
	http.HandleFunc("/add", addHandler)
	log.Fatal(http.ListenAndServe(":9090", nil))
}

客户端来请求上述HTTP服务

// client/main.go

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type addParam struct {
	X int `json:"x"`
	Y int `json:"y"`
}

type addResult struct {
	Code int `json:"code"`
	Data int `json:"data"`
}

func main() {
	// 通过HTTP请求调用其他服务器上的add服务
	url := "http://127.0.0.1:9090/add"
	param := addParam{
		X: 10,
		Y: 20,
	}
	paramBytes, _ := json.Marshal(param)
	resp, _ := http.Post(url, "application/json", bytes.NewReader(paramBytes))
	defer resp.Body.Close()

	respBytes, _ := ioutil.ReadAll(resp.Body)
	var respData addResult
	json.Unmarshal(respBytes, &respData)
	fmt.Println(respData.Data) // 30
}

这中交互模式,应该是非常常见了。

这种模式是我们目前比较常见的跨服务或跨语言之间基于RESTful API的服务调用模式。

既然使用API调用也能实现类似远程调用的目的,为什么还要用RPC呢?

使用 RPC 的目的是让我们调用远程方法像调用本地方法一样无差别

并且基于RESTful API通常是基于HTTP协议,传输数据采用JSON等文本协议,相较于RPC 直接使用TCP协议,传输数据多采用二进制协议来说,RPC通常相比RESTful API性能会更好。(在我的rabbitMQ文章中有体现)

RESTful API多用于前后端之间的数据传输,而目前微服务架构下各个微服务之间多采用RPC调用

使用场景

  1. 服务化/微服务
  2. 分布式系统架构
  3. 服务可重用
  4. 系统间交互调用

总结一下:

  1. 内部子系统较多
    • 众多的内部子系统是驱动采用RPC架构的原因之一,订单系统,支付系统,商品系统,用户系统…, 每个可独立单独布署。 RPC主要使用在大型企业内部子系统之间的调用。
    • 基于HTTP协议的接口,包括Webservice等主要作为对外接口服务。
  2. 接口访问量巨大
    • 要求满足支持 “负载均衡”,“熔断降级” 一类面向服务的高级特性
  3. 接口非常多
    • 服务发现

RPC原理

在这里插入图片描述

  1. 服务调用方(client)以本地调用方式调用服务
  2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体,
  3. client stub找到服务地址,并将消息发送到服务端
  4. server端接收到消息
  5. server stub收到消息后进行解码
  6. server stub根据解码结果调用本地的服务
  7. 本地服务执行并将结果返回给server stub
  8. server stub将返回结果打包成能够进行网络传输的消息体
  9. 按地址将消息发送至调用方
  10. client 端接收到消息
  11. client stub收到消息并进行解码
  12. 调用方得到最终结果

使用RPC框架的目标是只需要关心第1步第12步,中间的其他步骤统统封装起来,让使用者无需关心。

例如社区中各式RPC框架(grpc、thrift等)就是为了让RPC调用更方便。

RPC的目标就是要2~8这些步骤都封装起来,让用户对这些细节透明。

如何做到透明化(封装)远程服务调用

怎么封装通信细节才能让用户像以本地调用方式调用远程服务呢?

这部分有点像我用java实现的MQ一样。

就是定义一个结构体。

如果我们需要从A调用B的方法,那么意味着B需要A传来的参数和方法名等,然后通过反射来解析。

所以我们就可以这么做。

能看到这里的人相信对如何设置数据结构已经了熟与心了,所以就不写代码了。(用web实现数据结构的肯定没少做这个事)

如果还是不知道。可以看我的这篇文章,并找到创建请求和响应协议参数这一节,当然看完也可。

对消息进行编码和解码

客户端的请求消息结构一般需要包括以下内容:

  • 接口名称:在我们的例子里接口名是“HelloWorldService”,如果不传,服务端就不知道调用哪个接口了;
  • 方法名:一个接口内可能有很多方法,如果不传方法名服务端也就不知道调用哪个方法;
  • 参数类型和参数值:参数类型有很多,比如有 bool、int、long、double、string、map、list,甚至如struct等,以及相应的参数值;
  • 超时时间 + requestID(标识唯一请求id)

服务端返回的消息结构一般包括以下内容:

  • 状态code
  • 返回值
  • requestID

一旦确定了消息的数据结构后,下一步就是要考虑序列化与反序列化了。

序列化

什么是序列化?

  • 序列化就是将数据结构或对象转换成二进制串的过程,也就是编码的过程。

什么是反序列化?

  • 将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

为什么需要序列化?

  • 转换为二进制串后才好进行网络传输(快)

为什么需要反序列化?

  • 将二进制转换为对象才好进行后续处理(方便)

现如今序列化的方案越来越多,每种序列化方案都有优点和缺点,它们在设计之初有自己独特的应用场景,那到底选择哪种呢?

从RPC的角度上看,主要看三点:

  • 通用性:比如是否能支持Map等复杂的数据结构;
  • 性能:包括时间复杂度和空间复杂度,由于RPC框架将会被公司几乎所有服务使用,如果序列化上能节约一点时间,对整个公司的收益都将非常可观,同理如果序列化上能节约一点内存,网络带宽也能省下不少
  • 可扩展性:对互联网公司而言,业务变化飞快,如果序列化协议具有良好的可扩展性,支持自动增加新的业务字段,而不影响老的服务,这将大大提供系统的灵活度。

目前互联网公司广泛使用ProtobufThriftAvro等成熟的序列化解决方案来搭建RPC框架,这些都是久经考验的解决方案。

这里就不介绍了。

如果想要知道如何创建,搭建一个框架的,个人建议可以去这里,极客兔兔的文章。

下一章介绍如何使用go中的RPC的包

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

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

相关文章

一种LED驱动专用控制电路方案

一、基本的概述 TM1651 是一种带键盘扫描接口的LED&#xff08;发光二极管显示器&#xff09;驱动控制专用电路&#xff0c;内部集成有MCU 数字接口、数据锁存器、LED 高压驱动、键盘扫描等电路。本产品性能优良&#xff0c;质量可靠。采用SOP16/DIP16的封装形式。 二、特性说…

(C++)三数之和--双指针法

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 算法原理 双指针法&#xff0c;不一定是说就要使用指针&#xff0c;只是一种形象的说法&#xff0c;在数组中&#xff0c;我们一般将数组下标当做指针。我们首先对数组进行排序&#xff0c;从左向右标定一个下标i&#xff0…

算法:笛卡尔平面坐标系上,若干连接点形成线,剔除距离小于阈值的点,Kotlin

算法&#xff1a;笛卡尔平面坐标系上&#xff0c;若干连接点形成线&#xff0c;剔除距离小于阈值的点&#xff0c;Kotlin const val THRESHOLD 0.6f //距离小于这个点将被剔除。data class Point(val x: Float, val y: Float)fun removeNearbyPoint(points: List<Point>…

pta模拟题(7-15 BCD解密、7-16 数列求和-加强版、7-17 出租、7-18 反向数相加、7-19 一帮一、7-20 连续因子)

7-15 BCD解密 BCD数是用一个字节来表达两位十进制的数&#xff0c;每四个比特表示一位。所以如果一个BCD数的十六进制是0x12&#xff0c;它表达的就是十进制的12。但是小明没学过BCD&#xff0c;把所有的BCD数都当作二进制数转换成十进制输出了。于是BCD的0x12被输出成了十进制…

基于相关性的四种机器学习聚类方法

在这篇文章中&#xff0c;基于20家公司的股票价格时间序列数据。根据股票价格之间的相关性&#xff0c;看一下对这些公司进行分类的四种不同方式。 苹果&#xff08;AAPL&#xff09;&#xff0c;亚马逊&#xff08;AMZN&#xff09;&#xff0c;Facebook&#xff08;META&…

服务器基础知识

服务器被誉为互联网之魂。我第一次见到服务器是在学校图书馆&#xff0c;是一种机架式服务器&#xff0c;第二次见到服务器是在公司机房。本期文章是对服务器进行大盘点和梳理&#xff0c;会介绍我拆装服务器过程&#xff0c;从中的学习感悟。 图片来自 Pexels 01 服务器 服务…

Python开发运维:PyMongo 连接操作 MongoDB

目录 一、理论 1.PyMongo模块 2.Mongo Shell 二、实验 1. Windows11安装MongoDB 7.0.4 2.Windows11安装MongoDB Shell 2.1.0 3.PyMongo 连接 MongoDB&#xff08;无密码方式&#xff09; 4.PyMongo 连接 MongoDB&#xff08;有密码方式&#xff09; 5.PyMongo 操作 Mo…

Module build failed: Error: ENOENT: no such file or directory

前言 这个错误通常发生在Node.js 和 vue,js项目中&#xff0c;当你试图访问一个不存在的文件或目录时。在大多数情况下&#xff0c;这是因为你的代码试图打开一个不存在的文件&#xff0c;或者你的构建系统&#xff08;例如Webpack&#xff09;需要一个配置文件&#xff0c;但找…

java调用mysql存储过程

一、背景 在mysql上定义了存储过程&#xff0c;然后我想每1分钟调用一次存储过程。于是我设置了一个event&#xff0c;但是这个事件默认的运行周期为天&#xff0c;我尝试修改成minute却不生效。所以我决定通过java代码来调用存储过程。 二、mysql存储过程 CREATE DEFINERroot…

排序算法:n个0~1000之间的整数,将他们从大到小排序

上榜理由&#xff1a; 如果没见过这种排序题&#xff0c;可能首先想到的就是常用的排序算法&#xff0c;比如快速排序&#xff0c;归并排序&#xff0c;那如果输入的n足够大&#xff0c;时间复杂度肯定比较高。其实题目0-1000的范围是一个题眼&#xff0c;所以一定有更优的排序…

将本地项目推送到github

欢迎大家到我的博客浏览。将本地项目推送到github | YinKais Blog 本地项目上传至 GitHub<!--more--> 1、进入项目根目录&#xff0c;初始化本地仓库 git init 2、创建密钥&#xff1a;创建 .ssh 文件夹&#xff0c;并进入 .ssh 文件夹 mkdir .ssh cd .ssh/ 3、生成…

海云安谢朝海:开发安全领域大模型新实践 人工智能助力高效安全左移

2023年11月29日&#xff0c;2023中国&#xff08;深圳&#xff09;金融科技大会成功举行&#xff0c;该会议是深圳连续举办的第七届金融科技主题年度会议&#xff0c;也是2023深圳国际金融科技节重要活动之一。做好金融工作&#xff0c;需要兼顾创新与安全&#xff0c;当智能体…

深入理解Docker容器核心技术

文章目录 1. Linux命名空间&#xff08;Namespaces&#xff09;1.1 示例&#xff1a;PID命名空间 2. 控制组&#xff08;cgroups&#xff09;2.1 示例&#xff1a;内存控制组 3. 联合文件系统&#xff08;UnionFS&#xff09;3.1 示例&#xff1a;查看镜像的分层结构 4. Docker…

ASP.NET Core 使用IIS调试出现505.24错误

最近一直再学习asp.net 相关的东西&#xff0c;主要是为前端app提供一个webapi接口。在使用iis调试程序时出现HTTP Error 500.24 - Internal Server Error错误&#xff0c;搞了好久才最终解决。 1.在项目中增加web.config配置文件 2.将配置文件改为如下内容 <?xml version…

MATLAB 模型参考自适应控制 - Model Reference Adaptive Control

系列文章目录 文章目录 系列文章目录前言一、参考模型二、扰动与不确定性模型三、直接 MRAC名义模型参数更新间接 MRAC估计器模型和控制器增益参数更新学习修正参考文献 前言 模型参考自适应控制模块计算控制动作&#xff0c;使不确定的受控系统跟踪给定参考被控对象模型的行为…

C# Serilog--可记录异常完整路径

1.Serilog安装 2.控制台代码 --设置日志记录器的最小级别为 Debug&#xff0c;即只记录 Debug 级别及以上的日志信息 --.WriteTo.File("logs\\log.txt", rollingInterval: RollingInterval.Day)&#xff1a;将日志信息写入到指定路径的文件中&#xff08;这里的路径…

企业真题(数组\面向对象-基础)

二、企业真题 1. 数组有没有length()这个方法? String有没有length()这个方法&#xff1f;&#xff08;*蓝&#xff09; 数组没有length()&#xff0c;是length属性。 String有length() 2. 有数组int[] arr&#xff0c;用Java代码将数组元素顺序颠倒&#xff08;闪*购&…

软件测试计划书

测试计划书 1.测试参考文档和测试提交文档 2.测试进度计划 3.测试资源 4.系统风险、优先级 5.测试策略 6.缺陷管理 7.测试停止标准 软件开发全文档下载进入主页。

linux后端基础---笔记整理(tmux、vim、shell、ssh/scp、git、thrift、docker)

目录 1.Linux常用文件管理命令 2.tmux终端复用器/vim命令式文本编辑器 3.Shell语法 3.1 Shell—版本3.2 新建一个test.sh文件3.3 Shell文件—运行方式3.4 Shell—注释3.5 Shell—变量3.6 Shell—默认变量&#xff0c;文件参数, “$”的用法3.7 Shell—数组3.8 shell—expr命令…

WebSocket 前端使用vue3+ts+elementplus 实现连接

1.配置连接 websocket.ts文件如下 import { ElMessage } from "element-plus";interface WebSocketProps {url: string; // websocket地址heartTime?: number; // 心跳时间间隔&#xff0c;默认为 50000 msheartMsg?: string; // 心跳信息&#xff0c;默认为pingr…