聊聊什么是gRPC

news2025/4/17 19:05:39

前言

现如今,微服务变得越来越流行,而服务间的通信也变得越来越重要,服务间通信本质是交换信息,而交换信息的中介/桥梁正是我们的API

诚然,目前构建API最受欢迎的仍然是使用Restful(HTTP-JSON),因为它简单、快速、易懂。

但是在本文中,我们不妨尝试探索使用gRPC来构建我们的API,并在这个过程比较gRPC与Restful两者的异同。

最后,让我们来思考下,一个API应该是怎样的?如果从宏观的角度看,一个API应该是非常简单的,因为它就做了两件事:

  • 1.客户端发送一个请求(Request)
  • 2.服务端接收请求,并返回一个响应(Response)

这种思想在gRPC中也体现得非常明显,而从微观上看,构建一个API,我们可能需要考虑到:

  • 1.我们使用什么数据模型?JSON、XML还是二进制流
  • 2.我们使用什么协议传输?HTTP、TCP、还是HTTP/2
  • 3.我们如何调用方法,以及处理错误
  • 4.我们如何应对数据量大的情况
  • 5.我们如何减少接口的延时等
  • ...

话不多说,让我们进入正题。

什么是gRPC

在聊聊什么是gRPC前,我们先来聊聊什么是RPC。

RPC,全称Remote Procedure Call,中文译为远程过程调用。通俗地讲,使用RPC进行通信,调用远程函数就像调用本地函数一样,RPC底层会做好数据的序列化与传输,从而能使我们更轻松地创建分布式应用和服务。

而gRPC,则是RPC的一种,它是免费且开源的,由谷歌出品。使用gRPC,我们只需要定义好每个API的Request和Response,剩下的gRPC这个框架会帮我们自动搞定。

另外,gRPC的典型特征就是使用protobuf(全称protocol buffers)作为其接口定义语言(Interface Definition Language,缩写IDL),同时底层的消息交换格式也是使用protobuf。

gRPC基本通信流程

这是官方文档的一张图,通过这张图,我们可以大致了解下gRPC的通信流程:

1.gRPC通信的第一步是定义IDL,即我们的接口文档(后缀为.proto)

2.第二步是编译proto文件,得到存根(stub)文件,即上图深绿色部分。

3.第三步是服务端(gRPC Server)实现第一步定义的接口并启动,这些接口的定义在存根文件里面

4.最后一步是客户端借助存根文件调用服务端的函数,虽然客户端调用的函数是由服务端实现的,但是调用起来就像是本地函数一样。

以上就是gRPC的基本流程,从图中还可以看出,由于我们的proto文件的编译支持多种语言(Go、Java、Python等),所以gRPC也是跨语言的。

gRPC VS Restful

gRPC和Restful之间的对比,历来是学习gRPC的必修课,我会从文档规范、消息编码、传输协议、传输性能、传输形式、浏览器的支持度以及数据的可读性、安全性等方面进行比较。

文档规范

文档规范这种东西有点见仁见智,在我看来,gRPC使用proto文件编写接口(API),文档规范比Restful更好,因为proto文件的语法和形式是定死的,所以更为严谨、风格统一清晰;而Restful由于可以使用多种工具进行编写(只要人看得懂就行),每家公司、每个人的攥写风格又各有差异,难免让人觉得比较混乱

另外,Restful文档的过时相信很多人深有体会,因为维护一份不会过时的文档需要很大的人力和精力,而公司往往都是业务为先;而gRPC文档即代码,接口的更改也会体现到代码中,这也是我比较喜欢gRPC的一个原因,因为不用花很多精力去维护文档

消息编码

消息编码这块,gRPC使用protobuf进行消息编码,而Restful一般使用JSON进行编码

传输协议

传输协议这块,gRPC使用HTTP/2作为底层传输协议,据说也可替换为其他协议,但目前还未考证;而RestFul则使用HTTP

传输性能

由于gRPC使用protobuf进行消息编码(即序列化),而经protobuf序列化后的消息体积很小(传输内容少,传输相对就快);再加上HTTP/2协议的加持(HTTP1.1的进一步优化),使得gRPC的传输性能要优于Restful。

传输形式

传输形式这块,gRPC最大的优势就是支持流式传输,传输形式具体可以分为四种(unary、client stream、server stream、bidirectional stream),这个后面我们会讲到;而Restful是不支持流式传输的。

浏览器的支持度

不知道是不是gRPC发展较晚的原因,目前浏览器对gRPC的支持度并不是很好,而对Restful的支持可谓是密不可分,这也是gRPC的一个劣势,如果后续浏览器对gRPC的支持度越来越高,不知道gRPC有没有干饭Restful的可能呢?

消息的可读性和安全性

由于gRPC序列化的数据是二进制,且如果你不知道定义的Request和Response是什么,你几乎是没办法解密的,所以gRPC的安全性也非常高,但随着带来的就是可读性的降低,调试会比较麻烦;而Restful则相反(现在有HTTPS,安全性其实也很高)

代码的编写

由于gRPC调用的函数,以及字段名,都是使用stub文件的,所以从某种角度看,代码更不容易出错,联调成本也会比较低,不会出现低级错误,比如字段名写错、写漏。

gRPC的适用场景

从上面gRPC和Restful的比较中,我们其实也从侧面了解gRPC的优劣势,也能顺势推断出其应用场景。

总的来说,gRPC主要用于公司内部的服务调用,性能消耗低,传输效率高,服务治理方便。Restful主要用于对外,比如提供接口给前端调用,提供外部服务给其他人调用等,

gRPC简单实践

一般来讲,实现一个gRPC服务端和客户端,主要分为这几步:

  • 1.安装 protobuf 依赖
  • 2.编写 proto 文件(IDL)
  • 3.编译 proto 文件(生成stub文件)
  • 4.编写server端,实现我们的接口
  • 5.编写client端,测试我们的接口

1.安装 protobuf 依赖

# 1.安装protoc
$ brew install protoc

# 2.检查安装是否成功
$ protoc --version
libprotoc 3.7.1

# 3.安装编译插件
$ export GO111MODULE=on  # 开启Go Module
$ go get google.golang.org/protobuf/cmd/protoc-gen-go \
         google.golang.org/grpc/cmd/protoc-gen-go-grpc

2.编写 proto 文件

syntax = "proto3";

package greeter.srv;

option go_package = "proto/greeter";

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
    string name = 1;
}

// The response message containing the greetings
message HelloReply {
    string message = 1;
}

这里使用了官方文档的例子,算是比较简单的一个例子。首先第一行syntax = "proto3" 表明了我们使用的是proto3,而不是proto2,proto2是之前的版本。

而后面的service Greeter则表示定义一个Greeter的服务,这个服务下有SayHello接口,这个接口的请求是HelloRequest结构体,返回是HelloReply接口体(前面提到过API本质就是Request+Response!)。

同一份proto文件可以定义多个服务(不太建议,除非有关联),每个服务下面可以定义多个接口。

注:更多protobuf的语法,可以参考网上的其他教程

3.编译 proto 文件

编译命令是:

protoc --go_out=. --go_opt=paths=source_relative
--go-grpc_out=. --go-grpc_opt=paths=source_relative
proto/greeter/greeter.proto

注:proto文件的编译是我最想吐槽protobuf的一个点,首先是语法晦涩难懂,比如上述的paths=source_relative,其次是stub文件的存放位置,也需要多次尝试才能写入到我们想要的位置;最后是protobuf的各种插件,这些插件没办法输出版本,在使用的时候经常因为版本的不同遇到一些奇奇怪怪的问题。

(免费订阅,永久学习)学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂

更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! ! 

4.server端的编写

server端的编写如下:

// greeter_server.go
type server struct {
}

// 实现我们的接口
func (s *server) SayHello(ctx context.Context, req *greeter.HelloRequest) (rsp *greeter.HelloReply, err error) {
 rsp = &greeter.HelloReply{Message: "Hello " + req.Name}
 return rsp, nil
}

func main() {
 listener, err := net.Listen("tcp", ":52001")
 if err != nil {
  log.Fatalf("failed to listen: %v", err)
 }
 // gRPC 服务器
 s := grpc.NewServer()
 // 将服务器与处理器绑定
 greeter.RegisterGreeterServer(s, &server{})

 //reflection.Register(s)
 fmt.Println("gRPC server listen in 52001...")
 err = s.Serve(listener)
 if err != nil {
  log.Fatalf("failed to serve: %v", err)
 }
}

前面在proto文件中,我们只是定义了SayHello接口,并没有实现,所以当我们要实现一个server端,第一步就需要实现我们的接口。最后就是一个服务器的基本流程(不管是HTTP,还是gRPC),即声明一个服务器实例、绑定处理器以及最后的运行。

5.client端的编写

// greeter_client.go
func main() {
 // 发起连接,WithInsecure表示使用不安全的连接,即不使用SSL
 conn, err := grpc.Dial("127.0.0.1:52001", grpc.WithInsecure())
 if err != nil {
  log.Fatalf("connect failed: %v", err)
 }

 defer conn.Close()

 c := greeter.NewGreeterClient(conn)

 ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
 defer cancel()

    // 虽然SayHello由远程服务端实现,但调用起来就像一个本地函数一样
 r, err := c.SayHello(ctx, &greeter.HelloRequest{Name: "World"})
 if err != nil {
  log.Fatalf("call service failed: %v", err)
 }
 fmt.Println("call service success: ", r.Message)
}

client端的实现也比较简单,就是发起连接、创建客户端实例、调用方法,以及得到结果。

源代码

本文所有源代码均在本人的Github上,需要的话点击这里即可查阅

原文链接:https://zhuanlan.zhihu.com/p/363672930

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

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

相关文章

我学Python的那段日子(一)变量和简单数据类型

1.Python中的变量和简单数据类型(一) 1.1关于Python中的输出函数 使用 print()如: print("Hello,Python");在ide中显示的结果为 1.2 Python中的变量 所谓的变量即是可以用来保存数据的一个参数,变量的值是可变的。…

最短木板长度 华为OD真题 100

import java.util.Scanner; import java.util.*; import java.util.stream.Collectors;class Main {public static int min_num;public static void main(String[] args) {// 处理输入Scanner in new Scanner(System.in);int n in.nextInt();int m in.nextInt();in.nextLine…

小蓝本 第一本 《因式分解技巧》 第七章 综合运用 笔记 (第七天)

小蓝本 第一本 《因式分解技巧》 第七章 综合运用 笔记 (第七天)前言换元法好题例2题目解法经验补充例3题目解法经验例5题目解法补充例9题目解法经验补充例10题目解法补充例11题目解法补充例13题目解法习题7题目题解错题题号改错前言 12天攻掉《因式分解…

【笔记】计算机组成原理复习重点——篇1

计算机组成原理复习重点笔记 计算机组成原理计算机体系结构 学科基础必修课 研究生入学考试全国联考45分,占比30% 64学时,4学分,上课56,实验8 教材:计算机组成原理(第二版 ) 唐朔飞 高等教育出版社 目录 第1篇 概论 第…

如何确保RabbitMQ消息的可靠性?

开启生产者确认机制,确保生产者的消息能到达队列 开启持久化功能,确保消息未消费前在队列中不会丢失 开启消费者确认机制为auto,由spring确认消息处理成功后完成ack 开启消费者失败重试机制,并设置MessageRecoverer&#xff0c…

前端框架搭建(二)导入静态资源【vite】

1.根目录下创建styles目录 这里可根据你项目中的样式文件类型创建,因为这里我只有一个css没有scss 2.css目录下创建如下文件 global.css:全局所有的样式入口scrollbar.css:滚动条样式transition.css:动画样式 global.css impo…

Web3中文|盘点进军Web3的国际著名体育联盟

纵观如今的文化娱乐业,体育是最适合利用NFT实现粉丝参与和互动的领域。 NFT可以定格体育迷最喜欢的赛场高光时刻、记录体育迷所支持的球队或球星的美好瞬间,体育迷甚至还可以通过NFT体验交易收藏品的乐趣。 想知道这是怎么回事吗?让我们来粗…

查询 Linux 命令属于哪个软件包

在 Linux 中,有些命令的名称软件包的名称是不一样的,或者一个软件包中包含有多个命令。有时候,我们需要确定某个命令来自于哪个软件包,以便于可以在其他机器上安装,或者寻找该软件包的源代码进行编译或者修改。 下面以…

达美乐披萨:一家把自己“送”上市的企业

一、公司简介 达势股份,是知名披萨品牌在中国大陆、中国香港特别行政区和中国澳门特别行政区的独家总特许经营商,截至日前,公司在中国大陆14个城市拥有569家直营门店。 二、基本面分析 2.1 财务数据,扩张与亏损并存 **营收方面&a…

Sa-Token浅谈

主要介绍Sa-Token的鉴权使用以及实现原理。 文章目录简介使用源码解释创建会话1.前置检查2.获取配置3.分配token4.获取 User-Session5.设置token-id映射关系6.登录成功事件发布7.检查会话数量客户端注入Token简介 官网介绍的非常详细,主要突出这是一个轻量级鉴权框…

23个常见的 JavaScript 函数

本文收集了23个日常开发中非常常用的功能,其中一些可能很复杂,另一些可能很简单,但我相信它们都会或多或少对每个人都有帮助。 01、生成随机颜色 当网站需要生成随机颜色时,我们可以通过以下代码来执行此操作。 02、数组重新排序…

文件包含漏洞包含日志文件获取Shell实战

今天继续给大家介绍渗透测试相关知识,本文主要内容是文件包含漏洞包含日志文件获取Shell实战。 免责声明: 本文所介绍的内容仅做学习交流使用,严禁利用文中技术进行非法行为,否则造成一切严重后果自负! 再次强调&#…

Vue渲染器(四):双端diff算法

渲染器(四):双端diff算法 在上一章中,我们介绍了简单diff算法的实现原理。它利用vnode的key属性,尽可能多地复用DOM,并通过移动DOM的方式来完成更新,从而减少不断地创建和销毁DOM元素带来的性能…

(python + 雷电模拟器)frida下载与安装

frida下载 我这边是用pycharm下载的 我是直接下载最新的,暂时没发现什么异常 在安装成功界面查看frida版本 此时电脑端frida下载完成。打开github,搜索到frida,点击发行版 根据你的frida版本,对url进行修改进入你需要的版本…

方格涂色(冬季每日一题 30)

给定一个 nnnnnn 的方格矩阵,最初所有方格都是白色的。 现在需要将矩阵边界上的一些方格涂成黑色,从而使得: 最上一行恰好有 UUU 个方格是黑色的。最右一列恰好有 RRR 个方格是黑色的。最下一行恰好有 DDD 个方格是黑色的。最左一列恰好有 …

Android入门第47天-Fragment的基本使用

简介 我们的Android入门一步步已经进入中级。我们讲完了所有的基本组件的基本使用、Activity、Service、BroadCast。今天我们来到了Fragment篇章。Fragment和Activity比到底是一个什么样的存在呢?我们以一个很小的例子来说通Fragment。 Fragment是什么 Fragment可…

智能家居DIY系列之智能灯泡

一、什么是智能灯 传统的灯泡是通过手动打开和关闭开关来工作。有时,它们可以通过声控、触控、红外等方式进行控制,或者带有调光开关,让用户调暗或调亮灯光。 智能灯泡内置有芯片和通信模块,可与手机、家庭智能助手、或其他智能…

浅析JWT Attack

前言 在2022祥云杯时遇到有关JWT的题,当时没有思路,对JWT进行学习后来对此进行简单总结,希望能对正在学习JWT的师傅们有所帮助。 JWT JWT,即JSON WEB TOKEN,它是一种用于通信双方之间传递安全信息的简洁的、URL安全…

创新研发负载分担机制,天翼云IPv6网络带宽再升级!

网络作为社会信息化的基础,已成为人们日常生活不可或缺的一部分。网络通过模拟信号将信息转为电流进行传播,在这个过程中,网卡便充当了解码器的作用,能够将电信号转换为计算机能够识别的数字信号。 网卡,即网络接口卡&…

关于LabVIEW大作业/课设/论文的写作框架整理(主体三部曲)

文章目录 一、前言二、写作框架2.1 介绍函数以及工具箱2.2 介绍相关原理2.3 系统设计和案例演示三、总结一、前言 因为在Labview临近要交大作业,发现自己根本不会写,程序等的已经准备好了,但是对于写作一直不知道查了查知网文章,让我有了个大概了解,在此帖出来,希望能帮…