go-grpc实践指南-01grpc+protobuf介绍
目录
安装
- 第一步安装protoc编译器:
linux下载地址
windows64下载地址 - 安装protoc-gen-go和protoc-gen-go-grpc两个插件
go get google.golang.org/protobuf
go get google.golang.org/grpc
go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
grpc简介
这里着重说一下grpc采用Http/2的优势:
grpc采用http2标准设计,所以相对于其它rpc框架,grpc带来了更多强大的功能,如:双向流、头部压缩、多复用请求等。这些功能给移动设备带来重大溢出,如节省带宽、降低tcp链接次数、节省CPU和延长电池使用寿命等,同时grpc还能够提高云端服务和web应用的性能,grpc既能够在客户端应用,也能够在服务端应用,从而以透明的方式实现客户端和服务端的通信和简化通信系统的构建。
protobuf--->go
这里使用一个测试文件对照说明常用结构的protobuf到golang的转换。
- package
在proto文件中使用package关键字声明包名,默认转换成go中的包名与此一致,如果需要指定不一样的包名,可以使用go_package选项:
package test;
option go_package="test";
- Message
proto中的message对应go中的struct,全部使用驼峰命名规则,嵌套定义的message、enum转换为go之后,名称变为Parent_Child结构
示例proto:
syntax = "proto3";
package proto;
option go_package = "my_grpc/proto";
message Test {
int32 age = 1;
int64 count = 2;
double money = 3;
float score = 4;
string name = 5;
bool fat = 6;
bytes char = 7;
// Status:枚举状态
enum Status {
Ok = 0;
Fail = 1;
}
Status status = 8;
// Child子结构
message Child {
string sex = 1;
}
Child child = 9;
map<string, string> dict = 10;
}
转换结果:
// Status:枚举状态
type Test_Status int32
const (
Test_Ok Test_Status = 0
Test_Fail Test_Status = 1
)
type Test struct {
Age int32 `protobuf:"varint,1,opt,name=age,proto3" json:"age,omitempty"`
Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
Money float64 `protobuf:"fixed64,3,opt,name=money,proto3" json:"money,omitempty"`
Score float32 `protobuf:"fixed32,4,opt,name=score,proto3" json:"score,omitempty"`
Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"`
Fat bool `protobuf:"varint,6,opt,name=fat,proto3" json:"fat,omitempty"`
Char []byte `protobuf:"bytes,7,opt,name=char,proto3" json:"char,omitempty"`
Status Test_Status `protobuf:"varint,8,opt,name=status,proto3,enum=pb.Test_Status" json:"status,omitempty"`
Child *Test_Child `protobuf:"bytes,9,opt,name=child,proto3" json:"child,omitempty"`
Dict map[string]string `protobuf:"bytes,10,rep,name=dict,proto3" json:"dict,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
// Child子结构
type Test_Child struct {
Sex string `protobuf:"bytes,1,opt,name=sex,proto3" json:"sex,omitempty"`
}
除了会生成对应的结构外,还会有些工具方法,如字段的getter:
func (x *Test) GetAge() int32 {
if x != nil {
return x.Age
}
return 0
}
枚举类型会生成对应名称的常量,同时会有两个map方法使用:
// Enum value maps for Test_Status.
var (
Test_Status_name = map[int32]string{
0: "Ok",
1: "Fail",
}
Test_Status_value = map[string]int32{
"Ok": 0,
"Fail": 1,
}
)
- service
定义一个简单的Service,TestService有一个方法Test,接收一个Request参数,返回Response:
service TestService {
rpc Test(Request) returns(Response){}
}
message Request {
string name = 1;
}
message Response {
string message = 1;
}
转换结果:
# 客户端接口
type TestServiceClient interface {
Test(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}
# 服务端接口
type TestServiceServer interface {
Test(context.Context, *Request) (*Response, error)
mustEmbedUnimplementedTestServiceServer()
}
生成的go代码中包含该service定义的接口,客户端接口已经自动实现了,直接供客户端调用者调用,服务端接口需要由服务提供方实现。
服务端代码
package main
import (
"context"
"google.golang.org/grpc"
"my_grpc/pb"
"net"
)
type TestService struct{
pb.UnimplementedTestServiceServer
}
func (t *TestService) Test(ctx context.Context, in *pb.Request) (*pb.Response, error) {
return &pb.Response{Message: "hello " + in.Name}, nil
}
func main() {
listen, _ := net.Listen("tcp", ":9000")
gServer := grpc.NewServer()
pb.RegisterTestServiceServer(gServer, &TestService{})
_ = gServer.Serve(listen)
}
客户端代码
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"my_grpc/pb"
)
func main() {
clientConn, _ := grpc.Dial(":9000", grpc.WithTransportCredentials(insecure.NewCredentials()))
testServiceClient := pb.NewTestServiceClient(clientConn)
reply, _ := testServiceClient.Test(context.Background(), &pb.Request{Name: "哈哈"})
fmt.Println(reply)
}
protobuf语法
proto中的数据结构对应不同语言的数据类型
.proto | C++ | Java | Python | Go | Ruby | C# |
---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double |
float | float | float | float | float32 | Float | float |
int32 | int32 | int | int | int32 | Fixnum or Bignum | int |
int64 | int64 | long | ing/long[3] | int64 | Bignum | long |
uint32 | uint32 | int[1] | int/long[3] | uint32 | Fixnum or Bignum | uint |
uint64 | uint64 | long[1] | int/long[3] | uint64 | Bignum | ulong |
sint32 | int32 | int | intj | int32 | Fixnum or Bignum | int |
sint64 | int64 | long | int/long[3] | int64 | Bignum | long |
fixed32 | uint32 | int[1] | int | uint32 | Fixnum or Bignum | uint |
fixed64 | uint64 | long[1] | int/long[3] | uint64 | Bignum | ulong |
sfixed32 | int32 | int | int | int32 | Fixnum or Bignum | int |
sfixed64 | int64 | long | int/long[3] | int64 | Bignum | long |
bool | bool | boolean | boolean | bool | TrueClass/FalseClass | bool |
string | string | String | str/unicode[4] | string | String(UTF-8) | string |
bytes | string | ByteString | str | []byte | String(ASCII-8BIT) | ByteString |
定义服务
如果想要将消息类型用在RPC(远程方法调用)系统中,可以在.proto文件中定义一个RPC服务接口,protocol编译器会根据所选择的不同语言生成服务接口代码。例如,想要定义一个RPC服务并具有一个方法,该方法接收SearchRequest并返回一个SearchResponse,此时可以在.proto文件中进行如下定义:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse) {}
}
生成的接口代码作为客户端与服务端的约定,服务端必须实现定义的所有接口方法,客户端直接调用同名方法向服务端发起请求。