This page looks best with JavaScript enabled

初嚐Protocol Buffer好滋味

 ·  ☕ 4 min read

什麼是Protocol Buffer

Protocol Buffer 是由是 Google 所推出的一種輕量且高效的結構化資料存儲格式。

將結構化的資料進行序列化,實現資料的傳輸以及資料的儲存。

Protocol Buffer 比 XML、Json 更小、更快、使用及維護上更加的簡單(可以將 api speic 直接轉換成魏應的程式語言)!

優點

  • 體積小
  • 跨平台
  • 跨語言
  • 傳輸速度快
  • 維護成本低
  • 序列化速度快

定義資料結構

結構的定義很簡單,檔案以 .proto 作為後輟。

Coding style可以參考這裡:https://developers.google.com/protocol-buffers/docs/style

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
syntax = "proto3";
package tutorial;
message Employee {
    string name = 1;
    int32 id = 2;
    enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
    }

    message PhoneNumber {
        string number = 1;
        PhoneType type = 2;
    }

    repeated PhoneNumber phones = 3;
}

第一行指定您正在使用proto3語法

接下來package定義在golang裡所屬於的package

message定義傳輸的訊息

訊息內容有name、id、枚舉以及一組內部phone number的message

編譯所撰寫好的.proto

安裝編譯時所需要用的套件,也可以參考官方的方法決定安裝的方式。

1
go get -u github.com/golang/protobuf/protoc-gen-go





我們使用剛剛安裝的套件進行編譯成 go library 或是可以編成 java python …. 等的 library 以達到跨平台跨語言的支持,生成的 library 直接幫我們寫好壓縮以及解壓縮的方法開發者只要專心撰寫自己的業務邏輯即可,本篇文章以 go 作為範例。

1
protoc --go_out=. *.proto

編譯完成後的檔案會是樣的命名方式:employee.pb.go
檔案內容會如同下面的範例所示。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: employee.proto

package tutorial

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package

type Employee_PhoneType int32

const (
	Employee_MOBILE Employee_PhoneType = 0
	Employee_HOME   Employee_PhoneType = 1
	Employee_WORK   Employee_PhoneType = 2
)

var Employee_PhoneType_name = map[int32]string{
	0: "MOBILE",
	1: "HOME",
	2: "WORK",
}
var Employee_PhoneType_value = map[string]int32{
	"MOBILE": 0,
	"HOME":   1,
	"WORK":   2,
}

func (x Employee_PhoneType) String() string {
	return proto.EnumName(Employee_PhoneType_name, int32(x))
}
func (Employee_PhoneType) EnumDescriptor() ([]byte, []int) {
	return fileDescriptor_employee_7c804fd7a46a4aa3, []int{0, 0}
}

type Employee struct {
	Name                 string                  `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
	Id                   int32                   `protobuf:"varint,2,opt,name=id" json:"id,omitempty"`
	Phones               []*Employee_PhoneNumber `protobuf:"bytes,3,rep,name=phones" json:"phones,omitempty"`
	XXX_NoUnkeyedLiteral struct{}                `json:"-"`
	XXX_unrecognized     []byte                  `json:"-"`
	XXX_sizecache        int32                   `json:"-"`
}

func (m *Employee) Reset()         { *m = Employee{} }
func (m *Employee) String() string { return proto.CompactTextString(m) }
func (*Employee) ProtoMessage()    {}
func (*Employee) Descriptor() ([]byte, []int) {
	return fileDescriptor_employee_7c804fd7a46a4aa3, []int{0}
}
func (m *Employee) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_Employee.Unmarshal(m, b)
}
func (m *Employee) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_Employee.Marshal(b, m, deterministic)
}
func (dst *Employee) XXX_Merge(src proto.Message) {
	xxx_messageInfo_Employee.Merge(dst, src)
}
func (m *Employee) XXX_Size() int {
	return xxx_messageInfo_Employee.Size(m)
}
func (m *Employee) XXX_DiscardUnknown() {
	xxx_messageInfo_Employee.DiscardUnknown(m)
}

var xxx_messageInfo_Employee proto.InternalMessageInfo

func (m *Employee) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}

func (m *Employee) GetId() int32 {
	if m != nil {
		return m.Id
	}
	return 0
}

func (m *Employee) GetPhones() []*Employee_PhoneNumber {
	if m != nil {
		return m.Phones
	}
	return nil
}

type Employee_PhoneNumber struct {
	Number               string             `protobuf:"bytes,1,opt,name=number" json:"number,omitempty"`
	Type                 Employee_PhoneType `protobuf:"varint,2,opt,name=type,enum=tutorial.Employee_PhoneType" json:"type,omitempty"`
	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
	XXX_unrecognized     []byte             `json:"-"`
	XXX_sizecache        int32              `json:"-"`
}

func (m *Employee_PhoneNumber) Reset()         { *m = Employee_PhoneNumber{} }
func (m *Employee_PhoneNumber) String() string { return proto.CompactTextString(m) }
func (*Employee_PhoneNumber) ProtoMessage()    {}
func (*Employee_PhoneNumber) Descriptor() ([]byte, []int) {
	return fileDescriptor_employee_7c804fd7a46a4aa3, []int{0, 0}
}
func (m *Employee_PhoneNumber) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_Employee_PhoneNumber.Unmarshal(m, b)
}
func (m *Employee_PhoneNumber) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_Employee_PhoneNumber.Marshal(b, m, deterministic)
}
func (dst *Employee_PhoneNumber) XXX_Merge(src proto.Message) {
	xxx_messageInfo_Employee_PhoneNumber.Merge(dst, src)
}
func (m *Employee_PhoneNumber) XXX_Size() int {
	return xxx_messageInfo_Employee_PhoneNumber.Size(m)
}
func (m *Employee_PhoneNumber) XXX_DiscardUnknown() {
	xxx_messageInfo_Employee_PhoneNumber.DiscardUnknown(m)
}

var xxx_messageInfo_Employee_PhoneNumber proto.InternalMessageInfo

func (m *Employee_PhoneNumber) GetNumber() string {
	if m != nil {
		return m.Number
	}
	return ""
}

func (m *Employee_PhoneNumber) GetType() Employee_PhoneType {
	if m != nil {
		return m.Type
	}
	return Employee_MOBILE
}

func init() {
	proto.RegisterType((*Employee)(nil), "tutorial.Employee")
	proto.RegisterType((*Employee_PhoneNumber)(nil), "tutorial.Employee.PhoneNumber")
	proto.RegisterEnum("tutorial.Employee_PhoneType", Employee_PhoneType_name, Employee_PhoneType_value)
}

func init() { proto.RegisterFile("employee.proto", fileDescriptor_employee_7c804fd7a46a4aa3) }

var fileDescriptor_employee_7c804fd7a46a4aa3 = []byte{
	// 208 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0xcd, 0x2d, 0xc8,
	0xc9, 0xaf, 0x4c, 0x4d, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x28, 0x29, 0x2d, 0xc9,
	0x2f, 0xca, 0x4c, 0xcc, 0x51, 0x7a, 0xc3, 0xc8, 0xc5, 0xe1, 0x0a, 0x95, 0x14, 0x12, 0xe2, 0x62,
	0xc9, 0x4b, 0xcc, 0x4d, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0xb3, 0x85, 0xf8, 0xb8,
	0x98, 0x32, 0x53, 0x24, 0x98, 0x14, 0x18, 0x35, 0x58, 0x83, 0x98, 0x32, 0x53, 0x84, 0xcc, 0xb8,
	0xd8, 0x0a, 0x32, 0xf2, 0xf3, 0x52, 0x8b, 0x25, 0x98, 0x15, 0x98, 0x35, 0xb8, 0x8d, 0xe4, 0xf4,
	0x60, 0x66, 0xe9, 0xc1, 0xcc, 0xd1, 0x0b, 0x00, 0x29, 0xf0, 0x2b, 0xcd, 0x4d, 0x4a, 0x2d, 0x0a,
	0x82, 0xaa, 0x96, 0x0a, 0xe7, 0xe2, 0x46, 0x12, 0x16, 0x12, 0xe3, 0x62, 0xcb, 0x03, 0xb3, 0xa0,
	0x96, 0x41, 0x79, 0x42, 0x06, 0x5c, 0x2c, 0x25, 0x95, 0x05, 0xa9, 0x60, 0x0b, 0xf9, 0x8c, 0x64,
	0x70, 0x19, 0x1e, 0x52, 0x59, 0x90, 0x1a, 0x04, 0x56, 0xa9, 0xa4, 0xcd, 0xc5, 0x09, 0x17, 0x12,
	0xe2, 0xe2, 0x62, 0xf3, 0xf5, 0x77, 0xf2, 0xf4, 0x71, 0x15, 0x60, 0x10, 0xe2, 0xe0, 0x62, 0xf1,
	0xf0, 0xf7, 0x75, 0x15, 0x60, 0x04, 0xb1, 0xc2, 0xfd, 0x83, 0xbc, 0x05, 0x98, 0x92, 0xd8, 0xc0,
	0xfe, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x5c, 0x1e, 0x53, 0x56, 0x11, 0x01, 0x00, 0x00,
}

小結

下一章節會使用本篇基於 protobuffer 建置出來的 go library 去撰寫一個簡單的 client server,並且透過 GRPC 去傳輸 protobuffer


Meng Ze Li
WRITTEN BY
Meng Ze Li
Kubernetes / DevOps / Backend

What's on this Page