【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插件
三个插件选择其中一个
1 2 3 4 5 6 7 8 | // 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库文件
1 | go get github.com/gogo/protobuf/proto |
4、生成文件和下载的插件选择的对应命令
1 2 3 4 5 6 7 8 | // go protoc --go_out=. *.proto // gogo protoc --gogo_out=. *.proto // gofast protoc --gofast_out=. *.proto |
5、遇到问题
配置代理
1 2 | go env -w GO111MODULE=on go env -w GOPROXY=https: //goproxy.cn,direct // 使用七牛云的 |
下载指定版本
1 2 | go install github.com/golang/protobuf/protoc-gen- go @v1.5.2 go install github.com/micro/micro/v3/cmd/protoc-gen-micro@master |
生成命令
1 2 3 | protoc -I . --go_out=plugins=grpc:. ./*.proto protoc --micro_out=./ --go_out=./ productService.proto protoc- go -inject-tag -input=../productService.pb. go |
三、具体案例编写
1 | protoc -I . --go_out=plugins=grpc:. protocol/*.proto |
- protoc是生成代码插件
- gogo_out是go语言代码插件
- plugins=grpc:./. go_out后面使用grpc插件将代码生成的放到和proto文件同目录
- protocol/*.proto。protobuf协议文件地址
1、定义消息协议
message.proto1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | syntax = "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
1 2 3 4 5 6 7 8 9 10 11 | syntax = "proto3" ; package protocol; option go_package = "./protocol" ; option java_package = "com.kone.pbdemo.protocol" ; message File { string name = 1; int32 size = 2; } |
2、生成协议代码
1 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | 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
1 2 3 4 5 6 7 | package main import "test12/service" func main() { service.StartServer() } |
cmd/client/main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 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、运行
运行客户端
1 2 | PS D:\cpz\ go -demo\test11\server> go run main. go Listen on 127.0.0.1:9988 |
运行服务端
1 2 3 4 5 6 7 8 | 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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2021-10-02 【Golang】Go 通过结构(struct) 实现接口(interface)