【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

浙公网安备 33010602011771号