【Golang】关于Go语言中使用gRPC的使用
一、gRPC介绍
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
gRPC有四种服务方法:
- Unary RPCs,一元RPC。客户端发送一个请求到服务端,服务端响应一个请求。
- rpc getUser (User) returns (User) {}
- Server streaming RPCs,服务端流RPC。客户端发送一个请求到服务端,获取到一个流去连续读取返回的消息,直到消息全部获取。gRPC保证单个请求的消息顺序。
- rpc getUsers (User) returns (stream User) {}
- Client streaming RPCs,客户端流RPC。客户端给服务器通过流写入连续的消息,一旦客户端完成了消息写入,就等待服务端读取完成然后返回一个响应。同时gRPC也会保证单个请求的消息顺序。
- rpc saveUsers (stream User) returns (User) {}
- Bidirectional streaming RPCs,双向流。客户端和服务端都可以通过 read-write流发送一个连续的消息。两个流之间的操作是相互独立的。所以,客户端和服务端可以同时进行流的读写。
- rpc saveUsers (stream User) returns (stream User) {}
二、插件及其生成代码
1、进入protobuf仓库,下载和电脑相对应的版本
https://github.com/protocolbuffers/protobuf/releases
2、安装gogoprotobuf插件
三个插件选择其中一个
// go官方 go get github.com/golang/protobuf/protoc-gen-go // 完全兼容google protobuf,它生成的代码质量和编解码性能均比goprotobuf高一些 go get github.com/gogo/protobuf/protoc-gen-gogo // 最快 go get github.com/gogo/protobuf/protoc-gen-gofast
3、安装gogoprotobuf库文件
go get github.com/gogo/protobuf/proto
4、生成文件和下载的插件选择的对应命令
// go protoc --go_out=. *.proto // gogo protoc --gogo_out=. *.proto // gofast protoc --gofast_out=. *.proto
5、遇到问题
配置代理
go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct // 使用七牛云的
下载指定版本
go install github.com/golang/protobuf/protoc-gen-go@v1.5.2 go install github.com/micro/micro/v3/cmd/protoc-gen-micro@master
生成命令
protoc -I . --go_out=plugins=grpc:. ./*.proto protoc --micro_out=./ --go_out=./ productService.proto protoc-go-inject-tag -input=../productService.pb.go
三、具体案例编写
protoc -I . --go_out=plugins=grpc:. protocol/*.proto
- protoc是生成代码插件
- gogo_out是go语言代码插件
- plugins=grpc:./. go_out后面使用grpc插件将代码生成的放到和proto文件同目录
- protocol/*.proto。protobuf协议文件地址
1、定义消息协议
message.protosyntax = "proto3"; package protocol; import "protocol/file.proto"; option go_package = "./protocol"; option java_multiple_files = true; option java_package = "com.kone.pbdemo.protocol"; message User { reserved 6 to 7; reserved "userId2"; int32 userId = 1; string username = 2; oneof msg { string error = 3; int32 code = 4; } string name = 8; UserType userType = 9; repeated int32 roles = 10; protocol.File file = 11; map<string, string> hobbys = 12; } enum UserType { UNKNOW = 0; ADMIN = 1; BUSINESS_USER = 2; }; service UserService { rpc getUser (User) returns (User) {} rpc getUsers (User) returns (stream User) {} rpc saveUsers (stream User) returns (User) {} } service FileService { rpc getFile(User) returns(File) {} }
file.proto
syntax = "proto3"; package protocol; option go_package = "./protocol"; option java_package = "com.kone.pbdemo.protocol"; message File { string name = 1; int32 size = 2; }
2、生成协议代码
protoc -I . --go_out=plugins=grpc:. protocol/*.proto
3、服务端方法实现
在协议中,RPC定义了3个方法:getUser, getUsers, saveUsers。
这三个方法分别对应gRPC四种服务的:
- getUser: 一元RPC。客户端一次请求返回单个用户信息的响应。
- getUsers: 服务端流。服务端返回连续的用户。
- saveUsers: 客户端流。客户端将批量保存的用户信息连续传输给服务端。
在user_service.go中分别实现这3个方法。
service/user_service.go
package service import ( "context" "fmt" "io" "net" "test12/protocol" "google.golang.org/grpc" ) type UserSever struct { } func (u *UserSever) GetUser(ctx context.Context, user *protocol.User) (*protocol.User, error) { fmt.Println("get user from server ", user) return &protocol.User{ Username: user.Username, Name: "the new name", }, nil } func (u *UserSever) GetUsers(user *protocol.User, stream protocol.UserService_GetUsersServer) error { user1 := &protocol.User{ Username: user.Username, Name: "stream new name", } if err := stream.Send(user1); nil != err { fmt.Println("send user1 error", err.Error()) return err } user2 := &protocol.User{ Username: user.Username, Name: "stream new name2", } if err := stream.Send(user2); nil != err { fmt.Println("send user2 error", err.Error()) return err } return nil } func (u *UserSever) SaveUsers(stream protocol.UserService_SaveUsersServer) error { for { user, err := stream.Recv() if err == io.EOF { return stream.SendAndClose(&protocol.User{ Username: "back username", Name: "name", }) } if nil != err { fmt.Println("SaveUsers error ", err.Error()) return err } fmt.Println("SaveUsers get user ", user) } } func NewServer() *UserSever { return &UserSever{} } func StartServer() { fmt.Println("start...") lis, err := net.Listen("tcp", "127.0.0.1:9091") if nil != err { fmt.Println(err.Error()) return } var opts []grpc.ServerOption grpcServer := grpc.NewServer(opts...) protocol.RegisterUserServiceServer(grpcServer, NewServer()) grpcServer.Serve(lis) }
cmd/server/main.go
package main import "test12/service" func main() { service.StartServer() }
cmd/client/main.go
package main import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "io" "test12/protocol" ) func main() { conn, err := grpc.Dial("127.0.0.1:9091", grpc.WithTransportCredentials(insecure.NewCredentials())) if nil != err { fmt.Println(err.Error()) } defer conn.Close() c := protocol.NewUserServiceClient(conn) user := &protocol.User{ Username: "user1", UserId: 10, } response, err2 := c.GetUser(context.Background(), user) if err2 != nil { fmt.Printf("get user error: %s \n", err2) return } fmt.Printf("response msg: %s \n", response) resStream, err3 := c.GetUsers(context.Background(), user) if nil != err3 { fmt.Println(err3.Error()) return } for { res, err4 := resStream.Recv() if err4 == io.EOF { break } if nil != err4 { fmt.Println(err4.Error()) break } fmt.Println("get stream response ", res) } resStream2, err4 := c.SaveUsers(context.Background()) if nil != err4 { fmt.Println(err4.Error()) return } resStream2.Send(user) resStream2.Send(user) reply, err5 := resStream2.CloseAndRecv() if nil != err5 { fmt.Println(err5.Error()) return } fmt.Println("SaveUsers replay ", reply) }
4、运行
运行客户端
PS D:\cpz\go-demo\test11\server> go run main.go Listen on 127.0.0.1:9988
运行服务端
PS D:\cpz\go-demo\test12\cmd\server> go run main.go start... get user from server userId:10 username:"user1" SaveUsers get user userId:10 username:"user1" SaveUsers get user userId:10 username:"user1" get user from server userId:10 username:"user1" SaveUsers get user userId:10 username:"user1" SaveUsers get user userId:10 username:"user1"
- 作者:踏雪无痕
- 出处:http://www.cnblogs.com/chenpingzhao/
- 本文版权归作者和博客园共有,如需转载,请联系 pingzhao1990#163.com