go微服务开山篇之protoBuf组件

protoBuf安装

下载安装

  1. 下载地址:https://github.com/protocolbuffers/protobuf/releases

    根据系统下载对应的安装文件,我现在是win10就下载了protoc-3.13.0-win64.zip

  2. 解压文件,如:E:\protoc-3.13.0-win64

  3. 添加环境变量或者将bin目录下的protoc.exe拷贝到gopath下的bin文件夹中,不用添加环境变量

    1. 在系统变量中新建:变量名:PROTOBUF_HOME 变量值:E:\protoc-3.13.0-win64
    2. 在系统变量中,编辑path,将前面建的PROTOBUF_HOME下的添加进去%PROTOBUF_HOME%\bin;
    3. cmd中执行protoc --version查看版本,如果可以看到版本号,说明环境变量没问题
  4. 安装protoc-gen-go

    1. 下载:go get -u github.com/golang/protobuf/protoc-gen-go
    2. gopath的bin生成protoc-gen-go.exe

proto命令

# 查看版本信息
protoc --version

# 生成对应语言的文件: --go_out=.   这是生成golang文件 .代表当前目录  hello.proto文件名
protoc --go_out=. hello.proto
# 或者
protoc hello.proto --go_out=.

proto语法

hello.proto

// 版本
syntax = "proto3";

/*
option设置包名,不设置默认会使用下一行的package后面的作为包名.
不设置会报一个警告:WARNING: Missing 'go_package' option in "hello.proto",
*/
option go_package = ".;hello";
package hello;

// message:一种消息类型,后面会详细说明类型
message Hello{
    
    
  // optional默认会加可以不写,写了生成
  optional string name = 1;
  int32 age = 2;
  string addr = 3;
}

说明:message 表示messages消息类型,后面跟消息名称

message类型

定义方式

// message后面跟消息名称
message xxx{
    
    
}

说明:

  • .proto文件中定义多种messages类型如:message、enum 和 service
  • 一个.proto文件中可以定义多个messages类型,建议一个文件中不要出现多种类型,会导致依赖性膨胀

字段定义

message Hello{
    
    
  optional string name = 1;
  required int32 age = 2;
  repeated string addr = 3;
}

字段详细说明

/*
	optional默认可以不写,如果写了生成需要带--experimental_allow_proto3_optional参数如:protoc --go_out=. hello.proto --experimental_allow_proto3_optional
	string 类型
	stringVal 变量名
	= 1 不是值是,=后面的数字表示字段编号
*/
optional string stringVal = 1;
1.字段规则:可以忽略,不用指定,默认为optional
	// optional 可以传或者不传值
    optional:字段可出现 0 次或1次,表示可选,为空可以指定默认值 [default = 10],不然使用语言的默认值
        optional int32 result_per_page = 3 [default = 10];
            字符串默认为空字符串
            数字默认为0
            bool默认为false
            枚举默认为第一个列出的值,一定要注意枚举的顺序,容易有坑
    // required 必须传值、而且只能传一次  注意proto3中已经无法使用required                                   
    required:字段只能也必须出现 1// repeated 可以传多个值或者不传值                                        
    repeated:字段可出现任意多次(包括 0),数组或列表要使用这种
2.类型:
    int32int64、sint32、sint64、string32-bit ....
3.字段名称:
    注意命名规范 
4.=后面的数字表示字段编号:
    0 ~ 536870911(除去 1900019999 之间的数字,预留的)


关于optional的说明:
对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,
消息中的其它字段正常处理。---因为optional字段的特性,
很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,
这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,
因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡

注释

// 单行注释
/* */ 多行注释

enum枚举类型

定义方式

enum EnumAllowingAlias {
    
    
  option allow_alias = true; 
  UNKNOWN = 0;
  STARTED = 1;
  RUNNING = 1;
}

你可以通过为不同的枚举常量指定相同的值来定义别名。
为此,你需要将 allow_alias 选项设置为true,否则 protocol 编译器将在找到别名时生成错误消息。

enum EnumAllowingAlias {
    
    
  option allow_alias = true;
  UNKNOWN = 0;
  STARTED = 1;
  RUNNING = 1;
}

enum EnumNotAllowingAlias {
    
    
  UNKNOWN = 0;
  STARTED = 1;
}

字段定义

UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;

都是常量,没有类型申明

注意:message中可以嵌套enum类型,里层的字段编号和外层的字段编号不冲突

service类型

定义方式

service TestService {
    
    
    //rpc 服务端对外的函数名(传入参数)returns(返回参数)
    rpc SayHello (HelloRequest) returns (HelloReply) {
    
    }
}

message HelloRequest {
    
    
  string name = 1;
}

message HelloReply {
    
    
  string message = 1;
}

字段定义

rpc SayHello (HelloRequest) returns (HelloReply) {
    
    }
//rpc 服务端对外的函数名   (传入参数) returns (返回参数){}

使用其他 Message 类型

message后面的名称可以在其他的类型中使用,这个时候类型就是指定的类型了

扫描二维码关注公众号,回复: 11755314 查看本文章
message SearchResponse {
  repeated Result result = 1;   // 这里的类型是下面定的Result类型,包含 Result message的所有
}

message Result {
  required string url = 1;
  optional string title = 2;
  repeated string snippets = 3;
}

字段类型

.proto 说明 Go语言 C++语言 Python语言 Java语言
double 浮点类型 float64 double float double
float 浮点类型 float32 float float float
int32 int32 int32 int int
int64 int64 int64 int/long long
uint32 uint32 uint32 int/long int
uint64 uint64 uint64 int/long long
sint32 int32 int32 int int
sint64 int64 int64 int/long long
fixed32 uint32 uint32 int/long int
fixed64 uint64 uint64 int/long long
sfixed32 int32 int32 int int
sfixed34 int64 int64 int/long long
bool bool bool bool boolean
string string string str/unicode String
bytes []byte string str ByteString

protoBuf组件小实战

proto文件

E:\workspace\src\sample\protoBuf_sample\hello.proto

syntax = "proto3";
option go_package = ".;hello";
package hello;

message Hello{
    
    
  optional string name = 1;
  int32 age = 2;
  repeated string addr = 3;
}

proto转go文件

E:\workspace\src\sample\protoBuf_sample 到这个目录执行命令

// 因为proto文件中有设置optional,转换就需要加--experimental_allow_proto3_optional参数
protoc --go_out=. hello.proto --experimental_allow_proto3_optional

go使用转换过来的文件

这是proto文件生成的
E:\workspace\src\sample\protoBuf_sample\hello.pb.go

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.23.0
// 	protoc        v3.13.0
// source: hello.proto

package hello

import (
	proto "github.com/golang/protobuf/proto"
	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)
)

// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4

type Hello struct {
    
    
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name *string  `protobuf:"bytes,1,opt,name=name,proto3,oneof" json:"name,omitempty"`
	Age  int32    `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
	Addr []string `protobuf:"bytes,3,rep,name=addr,proto3" json:"addr,omitempty"`
}

func (x *Hello) Reset() {
    
    
	*x = Hello{
    
    }
	if protoimpl.UnsafeEnabled {
    
    
		mi := &file_hello_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Hello) String() string {
    
    
	return protoimpl.X.MessageStringOf(x)
}

func (*Hello) ProtoMessage() {
    
    }

func (x *Hello) ProtoReflect() protoreflect.Message {
    
    
	mi := &file_hello_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
    
    
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
    
    
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Hello.ProtoReflect.Descriptor instead.
func (*Hello) Descriptor() ([]byte, []int) {
    
    
	return file_hello_proto_rawDescGZIP(), []int{
    
    0}
}

func (x *Hello) GetName() string {
    
    
	if x != nil && x.Name != nil {
    
    
		return *x.Name
	}
	return ""
}

func (x *Hello) GetAge() int32 {
    
    
	if x != nil {
    
    
		return x.Age
	}
	return 0
}

func (x *Hello) GetAddr() []string {
    
    
	if x != nil {
    
    
		return x.Addr
	}
	return nil
}

var File_hello_proto protoreflect.FileDescriptor

var file_hello_proto_rawDesc = []byte{
    
    
	0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x68,
	0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x4f, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x17, 0x0a,
	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e,
	0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20,
	0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72,
	0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x42, 0x07, 0x0a, 0x05,
	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
	file_hello_proto_rawDescOnce sync.Once
	file_hello_proto_rawDescData = file_hello_proto_rawDesc
)

func file_hello_proto_rawDescGZIP() []byte {
    
    
	file_hello_proto_rawDescOnce.Do(func() {
    
    
		file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData)
	})
	return file_hello_proto_rawDescData
}

var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_hello_proto_goTypes = []interface{
    
    }{
    
    
	(*Hello)(nil), // 0: hello.Hello
}
var file_hello_proto_depIdxs = []int32{
    
    
	0, // [0:0] is the sub-list for method output_type
	0, // [0:0] is the sub-list for method input_type
	0, // [0:0] is the sub-list for extension type_name
	0, // [0:0] is the sub-list for extension extendee
	0, // [0:0] is the sub-list for field type_name
}

func init() {
    
     file_hello_proto_init() }
func file_hello_proto_init() {
    
    
	if File_hello_proto != nil {
    
    
		return
	}
	if !protoimpl.UnsafeEnabled {
    
    
		file_hello_proto_msgTypes[0].Exporter = func(v interface{
    
    }, i int) interface{
    
    } {
    
    
			switch v := v.(*Hello); i {
    
    
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	file_hello_proto_msgTypes[0].OneofWrappers = []interface{
    
    }{
    
    }
	type x struct{
    
    }
	out := protoimpl.TypeBuilder{
    
    
		File: protoimpl.DescBuilder{
    
    
			GoPackagePath: reflect.TypeOf(x{
    
    }).PkgPath(),
			RawDescriptor: file_hello_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   1,
			NumExtensions: 0,
			NumServices:   0,
		},
		GoTypes:           file_hello_proto_goTypes,
		DependencyIndexes: file_hello_proto_depIdxs,
		MessageInfos:      file_hello_proto_msgTypes,
	}.Build()
	File_hello_proto = out.File
	file_hello_proto_rawDesc = nil
	file_hello_proto_goTypes = nil
	file_hello_proto_depIdxs = nil
}

E:\workspace\src\sample\main.go

package main

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	hello "sample/protoBuf_sample"
)

func main() {
    
    
	name := "张三"
	data := &hello.Hello{
    
    
		// Name *string 这是因为proto中设置optional
		Name: &name,
		Age:  18,
		Addr: []string{
    
    "北京", "上海", "广州"},
	}
	fmt.Println(data)

	// 编码
	byte_data, _ := proto.Marshal(data)
	//fmt.Println(byte_data)
	fmt.Printf("编码:%v\n", byte_data)

	var newData hello.Hello
	// 解码
	proto.Unmarshal(byte_data, &newData)
	fmt.Printf("解码:%v", newData)

	fmt.Println(newData.Name) //返回一个地址
	// 获取值
	fmt.Println(*newData.Name)
	fmt.Println(newData.GetName())

	// 将struct转换为字符串
	fmt.Println(newData.String())

	// Reset()会将struct的所有field的值清零
	//fmt.Println(newData.Reset)
}

/*
name:"张三"  age:18  addr:"北京"  addr:"上海"  addr:"广州"
编码:[10 6 229 188 160 228 184 137 16 18 26 6 229 140 151 228 186 172 26 6 228 184 138 230 181 183 26 6 229 185 191 229 183 158]
解码:{
    
    {
    
    {} [] [] 0xc00009c780} 0 [] 0xc0000886c0 18 [北京 上海 广州]}0xc0000886c0
张三
张三
name:"张三"  age:18  addr:"北京"  addr:"上海"  addr:"广州"
0x5cd390
*/

猜你喜欢

转载自blog.csdn.net/Maggie_up/article/details/108696369