1. 什么是Protobuf
protobuf是Google公司提出的一种轻便高效的结构化数据存储格式,常用于结构化数据的序列化,具有语言无关、平台无关、可扩展性特性,常用于通讯协议、服务端数据交换场景。
protobuf的核心内容包括:
- 定义消息:消息的结构体,以message标识。
- 定义接口:接口路径和参数,以service标识。
通过protobuf提供的机制,服务端与服务端之间只需要关注接口方法名(service)和参数(message)即可通信,而不需关注繁琐的链路协议和字段解析,极大降低了服务端的设计开发成本。
通过proto我们可以为不同的语言生成相应的文件来方便我们的grpc调用。
2. 一个简单的proto文件
syntax = "proto3"; //定义了我们使用的Protocol Buffers版本。
//有许多的可选项用来配置编译时的一些行为,这里因为是为go生成对应的文件,所以指明了生成文件所属的包
option go_package = "./;simple";
//表明我们定义了一个命名为Simple的服务(接口),内部有一个远程rpc方法,名字为SayHello。
//我们只要在server端实现这个接口,在实现类中书写我们的业务代码。在client端调用这个接口。
service Simple{
//里面是这个类的具体可供rpc调用的方法
rpc SayHello(HelloRequest) returns (HelloReplay){}
}
//请求的结构体
message HelloRequest{
//在消息体中,每个字段都必须要有一个唯一的标识号
string name = 1;
}
//返回的结构体
message HelloReplay{
string message = 1;
}
3.对proto文件进行编译
- 在Go中,package默认用作Go包名,除非你显式提供.proto文件中的选项go_package。
- 在Java和Kotlin中,package默认用作java包,除非你显式提供.proto文件中的选项java_package。
对于go语言我们需要安装对应的
protoc --proto_path=IMPORT_PATH
--cpp_out=DST_DIR
--java_out=DST_DIR
--python_out=DST_DIR
--go_out=DST_DIR
--ruby_out=DST_DIR
--objc_out=DST_DIR
--csharp_out=DST_DIR
path/to/file.proto
- IMPORT_PATH定义了一个文件夹,**当解析import的时候会在这个文件夹下查找.proto文件。(因为你的proto可能引用了其他的proto)**如果省略了,就会使用当前文件夹。多个import文件夹可以通过多次声明–proto_path来添加;它们会被按照顺序依次查找。-I=_IMPORT_PATH_可以用作缩写的–proto_path。
- **_out指明了对应的类型的文件输出的路径
- 你必须提供一个或多个.proto 文件作为输入。可以一次指定多个 .proto 文件。尽管文件是相对于当前目录命名的,但每个文件都必须在IMPORT_PATH之一中,以便编译器可以确定其规范名称。
4. go中proto的生成
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc v3.20.3
// source: simple.proto
package simple
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// 请求的结构体
type HelloRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
....
func _Simple_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelloRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SimpleServer).SayHello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Simple/SayHello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SimpleServer).SayHello(ctx, req.(*HelloRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Simple_serviceDesc = grpc.ServiceDesc{
ServiceName: "Simple",
HandlerType: (*SimpleServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SayHello",
Handler: _Simple_SayHello_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "simple.proto",
}
[下一次讲解如何实现go-go之间grpc的调用通讯]