开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
gRPC是谷歌开发的、一个现代的、开源的、高性能的远程过程调用(RPC)框架,可以在任何地方运行。gRPC使客户端和服务器应用程序能够透明地通信,并简化了连接系统的构建。官方GitHub地址。
gRPC支持多种语言,其中包括go语言的grpc-go。
准备工作
在golang语言中使用grpc需要先安装一个protocol buffer的东西,安装完后会有个protoc的命令可以用。
Protobuf 是谷歌的一种二进制编码技术,grpc的通讯就是通过protobuf进行的。.proto文件就是rotobuf的定义文件。其官方语法连接。
然后在下载两个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
准本工作就完成了,参考官网地址: grpc.io/docs/langua…
接下来就是编写.proto文件,然后用protoc命令进行相关grpc的go文件生成。
最后就是把生成的go文件引入到项目里,进行使用。
四种模式
下面以官方的例子进行四种模式的讲解,官方讲解连接,官方源码连接
proto文件编写
下面是官方的样例proto文件:route_guide.proto
关于一个路由指南的服务应用。
syntax = "proto3";
// go_package 设置了生成的go文件包名
// 更一般的定义方式是相对方式 go_package=".;routeguide"
// 在protoc生成命令中指定包的路径
option go_package = "google.golang.org/grpc/examples/route_guide/routeguide";
// grpc服务所用的包名,可自定义
package routeguide;
//定义一个服务 在这里叫路由指南
service RouteGuide {
// 定义rpc服务
// 模式1 客户端(参数)与服务端((返回值)都是非流式。
// 获取指定坐标特征值。参数为坐标,返回值为特征值
rpc GetFeature(Point) returns (Feature) {}
// 模式2 服务器到客户端的流式RPC,参数为非流式,返回值为流式。
// 获取给定矩形中的特征值。参数为矩形坐标返回值为流式数据,所有在该矩形面积中的坐标特征值。
rpc ListFeatures(Rectangle) returns (stream Feature) {}
// 模式3 客户端到服务端的流式RPC,参数为流式,返回值为非流式
// 获取路由详情。参数为流式坐标(一系列坐标),返回一个路由摘要。
rpc RecordRoute(stream Point) returns (RouteSummary) {}
// 模式3 客户端和服务端双向流式RPC, 参数与返回值都是流式。
// 路由交互。参数为一个流式路由信息,返回值为另一个路由信息。
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}
// message数据类型,该类型可嵌套其他数据类型。
// 对于go来说会映射成为一个struct
// 经纬度坐标点
message Point {
int32 latitude = 1; //维度
int32 longitude = 2; //经度
}
// 经纬度表示的矩形,lo,hi为两个坐标。
message Rectangle {
Point lo = 1;
Point hi = 2;
}
// 坐标点特征值。
message Feature {
string name = 1;
Point location = 2;
}
// 路由信息。
message RouteNote {
Point location = 1;
string message = 2;
}
// 路由摘要
message RouteSummary {
int32 point_count = 1;
int32 feature_count = 2;
int32 distance = 3;
int32 elapsed_time = 4;
}
上述proto文件中已经做了备注。主要是一个关于路由的服务。
其中服务定义部分。定义了四种类型的rpc服务。
1 客户端与服务端都发送非流式数据。即函数参数与返回值都是非流式数据。
2 客户端发送非流式数据,服务端发送流式数据。即参数为非流式数据返回值为流式数据。
3 客户端发送流式数据,服务端发送非流式数据。即参数为流式数据,返回值为非流式数据。
4 客户端与服务端都发送流式数据。即参数与返回值均为流式数据。
看到这,关于grpc的四种模式已经清楚了,无非是服务端与客户端的流式非流式数据的组合。总共四种组合。
protobuf定义完了,接下来就是生成go文件。
在route_guide目录下运行如下生成命令:
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
routeguide/route_guide.proto
运行完后会生成 route_guide.pb.go 和 route_guide_grpc.pb.go 文件。
.pb.go 与 _grpc.pb.go 是固定格式,前边的名字 route_guide与route_guide.proto文件名一致。
xxx.pb.go 包含数据类型数据。包括请求和响应数据类型。
xxx_grpc.pb.go 包含客户端与服务端相关的接口及方法。
接下来利用生成的go文件去实现一个服务端和一个客户端。
服务端实现
服务代码解析
服务端代码地址(github.com/grpc/grpc-g…)
服务端实现代码
其中 pb.UnimplementedRouteGuideServer为上一步中生成的代码。主要作用是确定自定义的结构体能够完全实现服务接口。其代码如下:
此处生成的这些server服务代码与之前定义的protobuf中的方法一一对应:
//模式一
//protobuf
rpc GetFeature(Point) returns (Feature) {}
//go
func (UnimplementedRouteGuideServer) GetFeature(context.Context, *Point) (*Feature, error) {
go中服务端入参为Point,为客户端发送过来的数据,返回值Feature为响应给服务端数据。
// 模式二
// protobuf
rpc ListFeatures(Rectangle) returns (stream Feature) {}
// go
func (UnimplementedRouteGuideServer) ListFeatures(*Rectangle, RouteGuide_ListFeaturesServer) error
go中Rectangle为服务端发送过来的数据,RouteGuide_ListFeaturesServer为流式数据对象,有Send方法可以调用,发送给前端数据。
//模式三
//protobuf
rpc RecordRoute(stream Point) returns (RouteSummary) {}
//go
func (UnimplementedRouteGuideServer) RecordRoute(RouteGuide_RecordRouteServer) error
go中RouteGuide_RecordRouteServer为流式对象,有两个函数 SendAndClose 和 Recv 函数,通过这两个函数与客户端交互。
//模式四
//protobuf
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
//go
func (UnimplementedRouteGuideServer) RouteChat(RouteGuide_RouteChatServer) error
go中RouteGuide_RouteChatServer为流式对象,有两个函数 Send 和 Recv 函数,通过这两个函数与客户端交互。
通过比较protobuf和go中的函数,可以发现一个规律,所有涉及到流传输的都会被封装成一个对象,当成参数传给服务函数。
当然以上内容不需要去记忆,使用时候go函数是自动生成的,按照名字去找函数,然后在自己的结构体中去实现并使用。
具体功能实现可以去看源码进行学习。
服务启动流程
有个routeGuideServer结构体,如何启动一个rpc服务端呢? 只需要四步。
- 监听一个端口
lis, err := net.Listen("tcp", 50051) - 创建GRPC基础服务
grpcServer := grpc.NewServer() - 注册自定义服务到GRPC服务
pb.RegisterRouteGuideServer(grpcServer, newServer()) - 启动服务
grpcServer.Serve(lis)
源码如下:
客户端实现
客户端启动流程
- 连接服务端,获取连接对象。
conn, err := grpc.Dial(*serverAddr, opts...)
- 获取客户端对象。
client := pb.NewRouteGuideClient(conn)
- 发送数据,收到数据。
feature, err := client.GetFeature(ctx, point)
stream, err := client.ListFeatures(ctx, rect);
feature, err := stream.Recv()
stream, err := client.RecordRoute(ctx);
stream.Send(point);
reply, err := stream.CloseAndRecv();
stream, err := client.RouteChat(ctx);
in, err := stream.Recv();
stream.Send(note);
stream.CloseSend();
发送数据,处理收到的数据,四种模式。具体使用参照官方使用代码。
结语
首先介绍了protobuf及.proto编写。
然后介绍了生成go文件名方法。
最后以官方例子介绍了四种模式,及服务端与客户端的代码编写。