go语言 | grpc原理介绍(三)

了解 gRPC 通信模式中的消息流

gRPC 支持四种通信模式,分别是简单 RPC、服务端流式 RPC、客户端流式 RPC 和双向流式 RPC。

简单 RPC

在gRPC中,一个简单的RPC调用遵循请求-响应模型,通常涉及以下几个关键步骤和组件:

  1. 请求头(Request Header): 客户端(Client)发起请求时,首先发送一个请求头,其中包含元数据,如目标服务、方法等。
  2. 长度前缀消息(Length-Prefixed Message):在请求头后面,客户端发送实际的RPC消息。这个消息一般会有一个长度前缀,以便服务端(Server)可以准确地解析它。
  3. 数据帧(Data Frames): 一个消息可能会分成多个数据帧进行传输,这取决于消息的大小和底层传输协议的限制。
  4. 流结束标志(EOS, End Of Stream):客户端在发送完所有数据帧后会设置一个EOS标志。这标志着客户端对该请求的“半关闭”状态,意味着客户端将不再发送更多数据,但仍然可以接收来自服务端的响应。
  5. 响应头(Response Header): 服务端在收到并解析完整的请求消息后,首先发送一个响应头。
  6. 响应消息(Response Message): 随后,服务端发送实际的响应消息,该消息也是一个长度前缀的格式。
  7. Trailers: 最后,服务端通过发送一个带有状态详细信息的Trailers头来结束通信。
    在这里插入图片描述

服务端流式 RPC

从 client 端的角度来看,简单 RPC 和服务端流式 RPC 具有相同的请求消息流。在这两种情况下,我们都会发送一条请求消息。主要区别在于 server 端。server 端会发送多条消息,而不是向 client 端发送一条响应消息。server 端一直等待,直到收到完整的请求消息,之后发送响应头和多个带长度前缀的消息。一旦 server 端发送带有状态详细信息的 Trailers 标头,通信就会结束。

在这里插入图片描述

客户端流式 RPC

在客户端流式 RPC 中,client 端向 server 端发送多条消息,server 端发送一条响应消息作为回复。client 端首先通过发送请求头帧建立与 server 端的连接。建立连接后,client 端会向 server 端发送多个长度前缀消息作为数据帧。最后,client 端通过在最后一个数据帧中发送一个 EOS 标志来半关闭连接。同时,server 端读取从 client 端接收到的消息。一旦接收到所有消息,server 端就会发送响应消息以及 Trailers 标头并关闭连接。
在这里插入图片描述

双向流式 RPC

在此模式中,client 端通过发送请求头帧来建立连接。一旦建立连接,client 端和 server 端都可以直接发送多个长度前缀消息,而无需等待对方完成。双方都可以自主结束连接,这意味着他们不能再发送任何消息。

gRPC 实现架构

如下图所示,gRPC 采用分层架构实现。

在这里插入图片描述
在gRPC的架构中,核心组件分为以下几层:

  1. gRPC Core 层: 这是gRPC架构中的底层,负责网络操作的所有底层细节。它提供了一组核心API和功能,如流控制、安全认证、以及其他低级网络操作。
  2. 语言绑定(Language Bindings): gRPC原生支持C/C++、Go和Java,但也提供了多种流行编程语言的语言绑定,包括Python,Ruby、PHP等。这些绑定通常是对gRPC Core层的C API的高级包装,使得在特定语言中使用gRPC更加方便。
  3. 应用层(Application Layer): 这一层位于语言绑定之上,处理应用逻辑和数据编码逻辑。开发人员通常使用IDL(接口定义语言)编译器,如Protocol Buffers编译器,为特定的数据结构生成源代码。这些生成的代码会被集成到应用层逻辑中,用于序列化和反序列化消息。

通过这种层次结构,gRPC实现了从底层网络操作到高级应用逻辑的完全抽象,允许开发人员集中精力在RPC逻辑的实现上,而无需担心底层的网络细节。这种架构不仅使得代码更易于管理和扩展,还支持跨语言和跨平台的通信,大大提高了开发效率。


Q&A

gRPC Metadata 是通过什么传输?

在这里插入图片描述
在gRPC中,Metadata是通过HTTP/2 headers和trailers来传输的。Metadata是键值对的集合,用于传递与请求或响应相关的附加信息。这些信息可能包括诸如认证令牌、自定义消息头、请求ID等。

  1. 请求头(Request Headers): 在一个gRPC调用开始时,客户端会发送HTTP/2 headers,其中可以包括初始化该调用所需的Metadata。例如,这里可能包含身份验证相关的令牌。
  2. 响应头(Response Headers): 与之类似,服务端在响应开始时也会发送HTTP/2 headers,这些也可以携带Metadata。这通常用于传递关于请求状态或其他重要信息的指示。
  3. Trailers: 在请求或响应完成时,可以通过HTTP/2 trailers发送额外的Metadata。这常用于携带状态信息,例如gRPC的状态码和错误消息。

在一个典型的gRPC调用中,客户端会首先发送包含Metadata的HTTP/2 headers,然后发送编码后的RPC message。服务端在接收到这些信息后,会解析Metadata和RPC message,并根据这些执行相应的操作,然后返回响应和响应的Metadata。

调用 grpc.Dial 会真正的去连接服务端吗?

会,但是是异步连接的,连接状态为正在连接。但如果你设置了 grpc.WithBlock 选项,就会阻塞等待(等待握手成功)。另外你需要注意,当未设置 grpc.WithBlock 时,ctx 超时控制对其无任何效果。

调用 ClientConn 不 Close 会导致泄露吗?

会,除非你的客户端不是常驻进程,那么在应用结束时会被动地回收资源。但如果是常驻进程,你又真的忘记执行 Close 语句,会造成的泄露。如下图:

不控制超时调用的话,会出现什么问题?

短时间内不会出现问题,但是会不断积蓄泄露,积蓄到最后当然就是服务无法提供响应了。如下图:
在这里插入图片描述

为什么默认的拦截器不可以传多个?

func chainUnaryClientInterceptors(cc *ClientConn) {interceptors := cc.dopts.chainUnaryIntsif cc.dopts.unaryInt != nil {interceptors = append([]UnaryClientInterceptor{cc.dopts.unaryInt}, interceptors...)}var chainedInt UnaryClientInterceptorif len(interceptors) == 0 {chainedInt = nil} else if len(interceptors) == 1 {chainedInt = interceptors[0]} else {chainedInt = func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error {return interceptors[0](ctx, method, req, reply, cc, getChainUnaryInvoker(interceptors, 0, invoker), opts...)}}cc.dopts.unaryInt = chainedInt
}

当存在多个拦截器时,取的就是第一个拦截器。因此结论是允许传多个,但并没有用。

真的需要用到多个拦截器的话,怎么办?

可以使用 go-grpc-middleware 提供的 grpc.UnaryInterceptor 和 grpc.StreamInterceptor 链式方法,方便快捷省心。

频繁创建 ClientConn 有什么问题?

这个问题我们可以反向验证一下,假设不共用 ClientConn 看看会怎么样?如下:

func BenchmarkSearch(b *testing.B) {for i := 0; i < b.N; i++ {conn, err := GetClientConn()if err != nil {b.Errorf("GetClientConn err: %v", err)}_, err = Search(context.Background(), conn)if err != nil {b.Errorf("Search err: %v", err)}}
}

输出结果

    ... connection error: desc = "transport: Error while dialing dial tcp :10001: socket: too many open files"... connection error: desc = "transport: Error while dialing dial tcp :10001: socket: too many open files"... connection error: desc = "transport: Error while dialing dial tcp :10001: socket: too many open files"... connection error: desc = "transport: Error while dialing dial tcp :10001: socket: too many open files"
FAIL
exit status 1

当你的应用场景是存在高频次同时生成/调用 ClientConn 时,可能会导致系统的文件句柄占用过多。这种情况下你可以变更应用程序生成/调用 ClientConn 的模式,又或是池化它,这块可以参考 grpc-go-pool 项目。

客户端请求失败后会默认重试吗?

会不断地进行重试,直到上下文取消。而重试时间方面采用 backoff 算法作为的重连机制,默认的最大重试时间间隔是 120s。

为什么要用 HTTP/2 作为传输协议?

许多客户端要通过 HTTP 代理来访问网络,gRPC 全部用 HTTP/2 实现,等到代理开始支持 HTTP/2 就能透明转发 gRPC 的数据。不光如此,负责负载均衡、访问控制等等的反向代理都能无缝兼容 gRPC,比起自己设计 wire protocol 的 Thrift,这样做科学不少。


总结

gRPC 是一个高性能的远程过程调用(RPC)框架,它主要依赖于两个关键技术:Protocol Buffers和HTTP/2。

  1. Protocol Buffers:这是一个高效的数据序列化协议,用于编码和解码消息。由于其二进制格式和强类型定义,它通常比传统的JSON或XML更高效,具有更小的数据尺寸和更快的序列化/反序列化速度。这种优化的数据表达方式在网络传输中起到了至关重要的作用,尤其是在需要低延迟和高吞吐量的场景中。
  2. HTTP/2: 相较于其前身HTTP/1.x,HTTP/2提供了多路复用(Multiplexing)功能,使得多个请求和响应可以在单一的TCP连接上并行传输。这减少了网络延迟,并允许更高效的资源利用。此外,HTTP/2还支持其他高级特性,如头部压缩、优先级设置等,进一步优化了网络性能。

结合这两个高效的协议,gRPC能够提供低延迟、高吞吐量和高可扩展性,使其成为一个理想的选择用于构建分布式系统和微服务架构。同时,多路复用和高效的数据序列化也使得gRPC非常适用于移动应用、IoT设备以及其他网络受限的场景。


参考

https://segmentfault.com/a/1190000019608421

https://learnku.com/articles/72847

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

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

相关文章

鸿蒙LiteOs读源码教程+向LiteOS中添加一个简单的基于线程运行时的短作业优先调度策略

一、鸿蒙Liteos读源码教程 鸿蒙的源码是放在openharmony文件夹下&#xff0c;openharmony下的kernel文件夹存放操作系统内核的相关代码和实现。 内核是操作系统的核心部分&#xff0c;所以像负责&#xff1a;资源管理、任务调度、内存管理、设备驱动、进程通信的源码都可以在…

升级 MacOS 系统后,playCover 内游戏打不开了如何解决

我们有些小伙伴在升级了 macOS 系统后大概率会遇到之前能够正常使用的 playCover 突然游戏打不开了&#xff0c;最近 mac 刚刚正式推出了 MacOS 14.1 ,导致很多用户打开游戏会闪退&#xff0c;我们其实只需要更新一下 playCover 就可以解决 playCover 正式版更新会比较慢所以我…

基于生成对抗网络的照片上色动态算法设计与实现 - 深度学习 opencv python 计算机竞赛

文章目录 1 前言1 课题背景2 GAN(生成对抗网络)2.1 简介2.2 基本原理 3 DeOldify 框架4 First Order Motion Model5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于生成对抗网络的照片上色动态算法设计与实现 该项目较为新颖&am…

【rust/esp32】初识slint ui框架并在st7789 lcd上显示

文章目录 说在前面关于slint关于no-std关于dma准备工作相关依赖代码结果参考 说在前面 esp32版本&#xff1a;s3运行环境&#xff1a;no-std开发环境&#xff1a;wsl2LCD模块&#xff1a;ST7789V2 240*280 LCDSlint版本&#xff1a;master分支github地址&#xff1a;这里 关于s…

【音视频 | opus】opus编码的Ogg封装文件详解

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

汇编语言(举个栗子)

汇编语言&#xff08;Assembly Language&#xff09;是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言&#xff0c;亦称为符号语言。在汇编语言中&#xff0c;用助记符代替机器指令的操作码&#xff0c;用地址符号或标号代替指令或操作数的地址。在不同…

基于51单片机土壤湿度检测及自动浇花系统仿真(带时间显示)

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;单片机浇花 获取完整源码源文件仿真源文件原理图源文件论文报告等 单片机土壤湿度检测及自动浇花系统仿真&#xff08;带时间显示&#xff09; 具体功能&#xff1a; &#xff08;1&#xff09;液晶第一行显示实际湿度&am…

信道编码译码及MATLAB仿真

文章目录 前言一、什么是信道编码&#xff1f;二、信道编码的基本逻辑—冗余数据1、奇偶检验码2、重复码 三、编码率四、4G 和 5G 的信道编码1、卷积码2、维特比译码&#xff08;Viterbi&#xff09;—— 概率译码3、LTE 的咬尾卷积码4、LTE 的 turbo 码 五、MATLAB 仿真1、plo…

0008Java安卓程序设计-ssm基于Android平台的健康管理系统

文章目录 **摘要**目录系统实现开发环境 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 摘要 首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,…

Git 的基本操作 ——命令行

Git 的工作流程 详解如下&#xff1a; 本地仓库&#xff1a;是在开发人员自己电脑上的Git仓库,存放我们的代码(.git 隐藏文件夹就是我们的本地仓库) 远程仓库&#xff1a;是在远程服务器上的Git仓库,存放代码(可以是github.com或者gitee.com 上的仓库,或者自己该公司的服务器…

【ElasticSearch系列-05】SpringBoot整合elasticSearch

ElasticSearch系列整体栏目 内容链接地址【一】ElasticSearch下载和安装https://zhenghuisheng.blog.csdn.net/article/details/129260827【二】ElasticSearch概念和基本操作https://blog.csdn.net/zhenghuishengq/article/details/134121631【三】ElasticSearch的高级查询Quer…

PTA:前序序列创建二叉树

前序序列创建二叉树 题目输入格式输出格式输入样例&#xff08;及其对应的二叉树&#xff09;输出样例 代码 题目 编一个程序&#xff0c;读入用户输入的一串先序遍历字符串&#xff0c;根据此字符串建立一个二叉树&#xff08;以二叉链表存储&#xff09;。 例如如下的先序遍…

SpringCloudAlibaba - 项目完整搭建(Nacos + OpenFeign + Getway + Sentinel)

目录 一、SpringCloudAlibaba 项目完整搭建 1.1、初始化项目 1.1.1、创建工程 1.1.2、配置父工程的 pom.xml 1.1.3、创建子模块 1.2、user 微服务 1.2.1、配置 pom.xml 1.2.2、创建 application.yml 配置文件 1.2.3、创建启动类 1.2.4、测试 1.3、product 微服务 1…

如何使用CodeceptJS、Playwright和GitHub Actions构建端到端测试流水线

介绍 端到端测试是软件开发的一个重要方面&#xff0c;因为它确保系统的所有组件都能正确运行。CodeceptJS是一个高效且强大的端到端自动化框架&#xff0c;与Playwright 结合使用时&#xff0c;它成为自动化Web、移动甚至桌面 (Electron.js) 应用程序比较好用的工具。 在本文中…

代码随想录算法训练营第23期day38|动态规划理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

目录 一、动态规划理论基础 1.动态规划的解题步骤 2.动态规划应该如何debug 二、&#xff08;leetcode 509&#xff09;斐波那契数 1.递归解法 2.动态规划 1&#xff09;确定dp数组以及下标的含义 2&#xff09;确定递推公式 3&#xff09;dp数组如何初始化 4&#x…

C++虚表与虚表指针详解

类的虚表 每个包含了虚函数的类都包含一个虚表。 当一个类&#xff08;B&#xff09;继承另一个类&#xff08;A&#xff09;时&#xff0c;类B会继承类A的函数的调用权。所以如果一个基类包含了虚函数&#xff0c;那么其继承类也可调用这些虚函数&#xff0c;换句话说&…

基于ASP.NET MVC + Bootstrap的仓库管理系统

基于ASP.NET MVC Bootstrap的仓库管理系统。源码亲测可用&#xff0c;含有简单的说明文档。 适合单仓库&#xff0c;基本的仓库入库管理&#xff0c;出库管理&#xff0c;盘点&#xff0c;报损&#xff0c;移库&#xff0c;库位等管理&#xff0c;有着可视化图表。 系统采用Bo…

MySQL导入数据库报错Error Code: 2006

Error Code: 2006 - MySQL server has gone away 因为导入的某张表数据过大导致导入中途失败 , 修改max_allowed_packet 即可解决。 SET GLOBAL max_allowed_packet 1024*1024*200;

【数据结构】数组和字符串(十五):字符串匹配2:KMP算法(Knuth-Morris-Pratt)

文章目录 4.3 字符串4.3.1 字符串的定义与存储4.3.2 字符串的基本操作4.3.3 模式匹配算法0. 朴素模式匹配算法1. ADL语言2. KMP算法分析3. 手动求失败函数定义例1例2例3 4. 自动求失败函数&#xff08;C语言&#xff09;5. KMP算法&#xff08;C语言&#xff09;6. 失败函数答案…

STM32F103C8T6第二天:按键点灯轮询法和中断法、RCC、电动车报警器(振动传感器、继电器、喇叭、433M无线接收发射模块)

1. 点亮LED灯详解&#xff08;307.11&#xff09; 标号一样的导线在物理上是连接在一起的。 将 PB8 或 PB9 拉低&#xff0c;就可以实现将对应的 LED 灯点亮。常用的GPIO HAL库函数&#xff1a; void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);//I/…