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
  1. IMPORT_PATH定义了一个文件夹,**当解析import的时候会在这个文件夹下查找.proto文件。(因为你的proto可能引用了其他的proto)**如果省略了,就会使用当前文件夹。多个import文件夹可以通过多次声明–proto_path来添加;它们会被按照顺序依次查找。-I=_IMPORT_PATH_可以用作缩写的–proto_path。
  2. **_out指明了对应的类型的文件输出的路径
  3. 你必须提供一个或多个.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的调用通讯]