前言
个人网站:linzyblog.netlify.app/
示例代码已经上传到github:点击跳转
gRPC官方文档:点击跳转
一、gRPC概述
1、什么是gRPC?
gRPC 是一个强大的开源 RPC(远程过程调用)框架,用于构建可扩展且快速的 API。它允许客户端和服务器应用程序透明地通信并开发连接的系统。gRPC框架依赖 HTTP/2
、协议缓冲区和其他现代技术堆栈来确保最大的 API 安全性、性能和可扩展性。
在 gRPC 中,客户端应用程序可以直接调用不同机器上的服务器应用程序上的方法,就像是本地对象一样,更容易创建分布式应用程序和服务
。
与许多 RPC 系统一样,gRPC 基于定义服务的思想,指定可以远程调用的方法及其参数和返回类型。在服务端,服务端实现这个接口并运行一个 gRPC 服务器来处理客户端调用。在客户端,客户端有一个存根(自动生成的文件),它提供与服务器相同的方法。
2、gRPC 的历史
2015 年,Google 开发了 gRPC 作为 RPC 框架的扩展,以链接使用不同技术创建的许多微服务。最初,它与 Google 的内部基础设施密切相关,但后来,它被开源并标准化以供社区使用。在其发布的第一年,顶级组织利用它来支持从微服务到 Web、移动和物联网的用例。并在 2017 年因越来越受欢迎而成为云原生计算基金会(CNCF)孵化项目。
3、使用Protobuf
Protobuf 是 Google 的序列化/反序列化协议
,可以轻松定义服务和自动生成客户端库。gRPC 使用此协议作为其接口定义语言 (IDL) 和序列化工具集。
- 客户端和服务器之间的 gRPC 服务和消息在 proto 文件中定义。
- Protobuf 编译器
protoc
生成客户端和服务器代码,在运行时将 .proto 文件加载到内存中,并使用内存中的模式来序列化/反序列化二进制消息
。 - 代码生成后,每条消息都会在客户端和远程服务之间进行交换。
为什么使用Protobuf?
使用 Protobuf 进行解析需要更少的 CPU 资源,因为数据被转换为二进制格式,并且编码的消息的大小更轻。因此,消息交换速度更快,即使在 CPU 速度较慢的机器(例如移动设备)中也是如此。
4、gRPC架构
在下面的 gRPC 架构图中,我们有 gRPC 客户端和服务器端。在 gRPC 中,每个客户端服务都包含一个存根(自动生成的文件),类似于包含当前远程过程的接口。
gRPC工作流程:
- gRPC 客户端将要发送到服务器的参数对存根进行本地过程调用。
- 客户端存根使用 Protobuf 使用
编组过程序列化参数
,并将请求转发到本地机器中的本地客户端时间库。 - 操作系统通过
HTTP/2
协议调用远程服务器机器。 - 服务器的操作系统接收数据包并调用服务器存根程序,该程序对接收到的参数进行解码并使用 Protobuf 执行相应的程序调用。
- 服务器存根将编码响应发送回客户端传输层。客户端存根取回结果消息并解包返回的参数,然后执行返回给调用者。
二、准备工作
使用 Go 来编写 gRPC Server 和 Client,让其互相通讯。在此之上会使用到如下库:
下面示例是在 windows
环境中安装。
google.golang.org/grpc google.golang.org/protobuf/cmd/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc
- 初始化项目
go mod init 项目名称
模块管理依赖项
go mod init go-grpc-examle
- 安装protoc:
➜ go get -u google.golang.org/grpc
通过--version
命令查看是否安装成功:
➜ protoc --version
libprotoc 3.20.1
- 使用以下命令为 Go 安装协议编译器插件:
➜ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
➜ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
验证插件是否安装成功:
➜ protoc-gen-go --version
protoc-gen-go.exe v1.28.1
➜ protoc-gen-go-grpc --version
protoc-gen-go-grpc 1.2.0
- 更新你的PATH,以便protoc编译器可以找到插件(你不在gopath下创建的项目,这里自行百度改一下PATH)
三、编写gRPC Client and Server
1、目录结构
go-grpc-example
├── client
│ └──hello_client
│ └── client.go
├── proto
│ └──hello
│ └── hello.proto
├── server
│ └──hello_server
│ └── server.go
2、编写 .proto 文件
syntax = "proto3";
// 定义go生成后的包名
option go_package = "./;hello";
package proto;
// 定义入参
message Request {
string name =1;
}
// 定义返回
message Response {
string result = 1;
}
// 定义接口
service UserService {
rpc Say(Request) returns (Response);
}
3、生成Go代码
# 同时生成hello.pb.go 和 hello_grpc.pb.go
➜ protoc --go-grpc_out=. --go_out=. hello.proto
当前目录下可以看到生成两个文件:
4、编写 Server 服务端代码
编写 gRPC Server 的基础模板,完成一个方法的调用。对 server.go 写入如下内容:
type HelloService struct {
// 必须嵌入UnimplementedUserServiceServer
hello.UnimplementedUserServiceServer
}
// 实现SayHi方法
func (h *HelloService) SayHi(ctx context.Context, req *hello.Request) (res *hello.Response, err error) {
format := time.Now().Format("2006-01-02 15:04:05")
return &hello.Response{Result: "hi " + req.GetName() + "---" + format}, nil
}
const PORT = "8888"
func main() {
// 创建grpc服务
server := grpc.NewServer()
// 注册服务
hello.RegisterUserServiceServer(server, &HelloService{})
// 监听端口
lis, err := net.Listen("tcp", ":"+PORT)
if err != nil {
log.Fatalf("net.Listen err: %v", err)
}
server.Serve(lis)
}
5、编写 Client 客户端代码
接下来编写 gRPC Go Client 的基础模板,打开 hello_client/client.go 文件,写入以下内容:
const PORT = "8888"
func main() {
// 建立链接
conn, err := grpc.Dial(":"+PORT, grpc.WithInsecure())
if err != nil {
log.Fatalf("grpc.Dial err: %v", err)
}
// 一定要记得关闭链接
defer conn.Close()
// 实例化客户端
client := hello.NewUserServiceClient(conn)
// 发起请求
response, err := client.SayHi(context.Background(), &hello.Request{Name: "lin钟一"})
if err != nil {
log.Fatalf("client.SayHi err: %v", err)
}
fmt.Printf("resp: %s", response.GetResult())
}
6、启动 & 请求
# 启动服务端
$ go run server.go
API server listening at: 127.0.0.1:50970
# 启动客户端
$ go run client.go
API server listening at: 127.0.0.1:51040
resp: hi lin钟一---2022-11-01 14:54:01
四、小结
在本文,我们对 gRPC Client/Server 进行了介绍和了解。希望你结合第一章写的rpc请求的方法对文中讲述内容再写一个 Demo 进行深入了解。
下一篇关于gRPC Streaming的内容,gRPC 三种类型的流式。
- Server-side streaming RPC:服务器端流式 RPC
- Client-side streaming RPC:客户端流式 RPC
- Bidirectional streaming RPC:双向流式 RPC