介绍
- gRPC is a high-performance open-source feature-rich RPC framework gRPC is originally developed by Google o
- Now it is a part of the Cloud Native Computing Foundation-CNCF
- g stands for different things in each gRPC release: gRPC, good, green, glorious, game, gon
- https://github. com/grpc/grpc/blob/master/doc/g_stands_for. md
- RPC stands for Remote Procedure Calls
gRPC, RPC 代表着远程调用的框架(Remote Procedure Calls),一种可以让一个程序远程调用另一个在其它服务器上程序的协议。而该框架会帮我们自动生成网络交互部分的具体代码,同时客户端仅需要一个能够调用这个服务的代码函数就可以了,服务端甚至不需要跟客户端是同一种编程语言。
如何实现这个功能的
客户端有一个存根(stub),它提供与服务器相同的方法(或函数)。这个存根是由 gRPC 自动为您生成的。
存根将在底层调用 gRPC 框架,以通过网络与服务器交换信息。
然后,神奇地,一切都可以正常工作。感谢存根,客户端和服务器现在只需要关注实现其核心服务逻辑。
然后我们需要了解的是 gRPC 存根如何通过 protobufcol buffer compiler (protobufc) 生成。然后根据不同的编程语言下载不同的插件进行生成
protobuf buffer
你可以把他当成一个代码生成工具以及序列化工具. 这个工具可以把我们定义的方法, 转换成特定语言的代码. 比如你定义了一种类型的参数他会帮你转换成 Golang 中的 struct 结构体, 你定义的方法, 他会帮你转换成 func 函数. 此外, 在发送请求和接受响应的时候, 这个工具还会完成对应的编码和解码工作, 将你即将发送的数据编码成 gRPC 能够传输的形式, 又或者将即将接收到的数据解码为编程语言能够理解的数据格式.
序列化: 将数据结构或对象转换成二进制串的过程 反序列化: 将在序列化过程中所产生的二进制串转换成数据结构或者对象的过程
安装 protobufcol buffer compiler (protobufc)
第一步需要安装 protobufbuf Compiler 如果是 windows 上面的话
下载
protobufc
编译器的 Windows 版本:下载适合 Windows 的
protobufc-x.x.x-win64.zip
文件(x.x.x 是版本号)。解压文件到一个目录,例如
C:\protobufbuf
。将
C:\protobufbuf\bin
添加到系统的PATH
环境变量中,这样你可以从命令行中直接运行protobufc
。
添加到 PATH:
右键单击 “此电脑” -> 属性 -> 高级系统设置 -> 环境变量。
在系统变量中找到
Path
,点击编辑,然后将C:\protobufbuf\bin
添加进去。
检查安装: 打开命令提示符,输入以下命令,确认
protobufc
安装成功:
|
|
windows 还要注意是否配置 GoPath 路径,否则在输入 protobufc 编译命令时可能显示无法识别该命令
如果是 MacOS 或 Linux 上的话
- 输入命令行
|
|
- 测试是否安装成功
|
|
Golang
之后统一在 cmd 或者 vscode 终端安装两个 GO 的插件
|
|
Python
要在 Python 中使用 protobufcol Buffers,首先需要安装 protobufbuf
包。可以通过 pip
进行安装:
|
|
如果需要 gRPC 支持,额外安装 grpcio
和 grpcio-tools
:
|
|
编写 .protobuf
文件
格式
「protobufcol buffer」本身书写格式可以参考 Language Guide (protobuf 3) | protobufcol Buffers Documentation protobufcol Buffers V3中文语法指南[翻译]
简单的一些 protof解释我结合实际代码和注释,如下:
|
|
protoc 命令
运行protoc
命令:
使用 protoc
命令编译 .proto
文件以生成客户端和服务端存根代码。
Python
|
|
是通过 Python 的 gRPC 插件 grpc_tools.protoc
来运行的,这个命令能够自动处理依赖项和路径问题,它与直接调用 protoc
编译器略有不同:
python -m grpc_tools.protoc
:这是使用 Python 包中的 gRPC 工具,通常会处理一些常见的路径和环境问题。- 它也确保我们使用的是 Python 环境中安装的
grpcio-tools
,而非系统中安装的独立protoc
编译器版本。
我在实际中使用第一个命令生成失败,第二个是成功的
Go
|
|
--go_out=.
:指定生成 Protocol Buffers 消息代码的目录。--go-grpc_out=.
:指定生成 gRPC 存根代码的目录。如果不指定这个参数,protoc
将会默认生成文件到当前目录。--proto_path=.
:指定.proto
文件的路径。- Go 的 gRPC 插件通常已经集成了 Protocol Buffers 和 gRPC 支持。
C++
|
|
--cpp_out=.
:指定生成 Protocol Buffers 消息代码的目录。--grpc_out=.
:指定生成 gRPC 存根代码的目录。如果不指定这个参数,protoc
将会默认生成文件到当前目录。--plugin=protoc-gen-grpc=
:指定 gRPC 插件路径。
为什么 C++需要指定 gRPC 插件地路径?
C++ 这里需要指定的原因在于 C++的 gRPC 插件是通过手动安装的独立二进制文件,并且安装路径通常不默认集成到
PATH
中。C++ 生态系统中的构建工具(如make
或CMake
)没有像 Python 的pip
或 Java 的Maven
那样的包管理系统自动配置环境。因此,用户需要在生成代码时显式地告诉protoc
该插件的位置。Python 和 Java 的插件通常会被安装到默认的环境中或者通过包管理工具来执行。这使得在运行
protoc
时,它能够直接通过系统的PATH
或相关的环境变量找到插件。所以不需要显示地指定
生成报错
|
|
❗ 在最开始生成代码是出现了上述的报错,原因在于未定义 go_package
由于
.proto
文件中缺少go_package
选项。gRPC 生成 Go 代码时,需要指定生成代码的包路径,这通常通过go_package
选项在.proto
文件中进行定义。在要生成的 proto 文件中,定义 option 字段 例如
option go_package = "pb/proto; proto";
gRPC 存根代码生成的流程图
让我们重新捋一遍存根代码生成的过程
|
|
调用
在上述的 proto 文件例子中,我们使用了 service 字段构建了一个简单 RPC(UnaryRPC),用于定义 gRPC 之间的通信,除此之外还有三种定义方法。
gRPC 四种类型的通信方式
简单 RPC(Unary RPC)
- 客户端:发送单个请求。
- 服务端:返回单个响应。
- 示例:客户端发送一个请求并接收一个响应,类似于传统的函数调用。适用于大多数简单的请求-响应交互。比如查询某个用户的信息或者是获取某个具体的资源。
|
|
服务端流式 RPC(Server-Side Streaming RPC)
- 客户端:发送单个请求。
- 服务端:返回流式响应,服务端可以向客户端发送多个响应。
- 示例:客户端发出一个请求,服务端用一系列响应来回答,客户端读取服务端发来的流数据。适合大量数据的连续推送,如实时数据推送、文件下载、日志流等。
|
|
客户端流式 RPC(Client-Side Streaming RPC)
- 客户端:发送流式请求,客户端可以发送多个请求。
- 服务端:返回单个响应。
- 示例:客户端发送一系列请求,服务端在接收完所有请求后返回一个响应。适合客户端需要批量上传数据的场景,如日志批量上传、文件上传等。
|
|
双向流式 RPC(Bidirectional Streaming RPC)
- 客户端:发送流式请求。
- 服务端:返回流式响应。
- 示例:客户端和服务端都可以同时读写,双方可以独立地发送消息,类似于一个双向通道。适合实时双向交互或并发处理的场景,如实时聊天、在线游戏、视频会议等。
|
|
通信方式 | 应用场景 | 举例 |
---|---|---|
简单RPC | 单个请求-单个响应的场景 | 获取用户信息、查询天气等 |
服务端流式RPC | 服务端持续推送数据的场景 | 实时股票推送、视频流媒体 |
客户端流式RPC | 客户端批量发送数据的场景 | 文件分块上传、批量日志上传 |
双向流式RPC | 双向实时数据传输的场景 | 实时聊天、视频会议、在线协作编辑 |
完整的一个调用例子(Go 作为 Client,Python 作为 Server)
1. Proto 文件 (example.proto
) 详解
|
|
- Proto 文件的作用:
example.proto
定义了 gRPC 通信的消息格式(HelloRequest
和HelloResponse
)和服务接口(ExampleService
),即客户端如何与服务端通信。HelloRequest
:包含一个字段name
,表示客户端发送的名字。HelloResponse
:包含一个字段message
,表示服务端返回的问候信息。service ExampleService
:定义了一个 gRPC 服务,其中包含一个 RPC 方法SayHello
,该方法接收HelloRequest
类型的请求,返回HelloResponse
类型的响应。
gRPC 使用这个 .proto
文件来生成客户端和服务端的接口代码,客户端使用该接口调用 RPC 方法,服务端实现该接口来处理请求。
2. Python 服务端代码详解
生成 Python 代码:
|
|
- 这条命令使用
protoc
工具生成 Python 代码。--python_out=.
:生成处理HelloRequest
和HelloResponse
消息的代码。--grpc_python_out=.
:生成 gRPC 服务器和客户端接口代码。- 生成的文件包含
example_pb2.py
和example_pb2_grpc.py
,其中example_pb2.py
处理消息格式,example_pb2_grpc.py
处理 gRPC 服务逻辑。
服务端实现:
|
|
ExampleServiceServicer
:这是ExampleService
的实现类,继承了自动生成的ExampleServiceServicer
基类。SayHello
方法接收客户端的HelloRequest
,并返回HelloResponse
,响应内容是Hello, {request.name}
。
serve
函数:启动 gRPC 服务器并使其监听在localhost:50051
端口上。grpc.server(futures.ThreadPoolExecutor(max_workers=10))
:创建一个 gRPC 服务器,并使用线程池来处理请求。add_ExampleServiceServicer_to_server
:将服务实现绑定到服务器。server.add_insecure_port('[::]:50051')
:监听 50051 端口。server.start()
:启动服务器。- 通过
time.sleep(86400)
保证服务器持续运行,直到收到KeyboardInterrupt
信号(Ctrl+C)后停止服务器。
Python 服务端执行流程:
- 服务端监听
localhost:50051
,等待客户端请求。 - 当客户端调用
SayHello
方法时,服务端接收请求,提取request.name
,并返回HelloResponse
。 - 服务器持续运行,等待更多请求。
3. Go 客户端代码详解
生成 Go 代码:
|
|
- 这条命令使用
protoc
生成 Go 代码。--go_out=.
:生成处理HelloRequest
和HelloResponse
消息的代码。--go-grpc_out=.
:生成 gRPC 客户端和服务端接口代码。
客户端实现:
|
|
grpc.Dial("localhost:50051", grpc.WithInsecure())
:建立与服务端的 gRPC 连接,连接localhost:50051
端口,WithInsecure
表示使用不加密的连接(在开发环境下常用)。pb.NewExampleServiceClient(conn)
:生成 gRPC 客户端存根,用于调用服务端的ExampleService
中定义的 RPC 方法。client.SayHello
:通过客户端调用SayHello
方法,传入一个HelloRequest
(即{Name: "Alice"}
),服务端接收该请求并返回一个HelloResponse
。context.WithTimeout
:设置请求的超时时间为 1 秒,以防止请求无限等待。
Go 客户端执行流程:
- 客户端与服务端建立连接。
- 调用
SayHello
方法,发送名字"Alice"
作为请求。 - 服务端处理请求并返回
Hello, Alice!
。 - 客户端打印出响应结果。