protpbuf简介

protobuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。
google 提供了多种语言的实现:Java、c#、c++、Go 和 Python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml、json等 进行数据交换快许多。
可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换
作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。

准备工作

  • 准备golang的基础环境,GOPATH等等
  • 准备protobuf底层库环境(conda或者源码编译)
  • 准备protobuf相关包和插件

准备基础环境

1
2
3
4
5
6
7
sh-4.2# go version
go version go1.8.3 linux/amd64
sh-4.2# go env
....
GOROOT="/usr/lib/golang"
GOPATH="/root/go"
....

准备protobuf底层库环境

1
2
3
4
5
6
# 因为工作原因会使用conda管理一些基础包,所以可以使用conda去安装基础模块

$ source /export/python2.7/setenv.sh
$ conda install libprotobuf -y
$ protoc --version
libprotoc 3.0.0

准备protobuf模块以及插件

1
2
3
4
# protoc-gen-go是用来将protobuf的的代码转换成go语言代码的一个插件
$ go get -u github.com/golang/protobuf/protoc-gen-go
# proto是protobuf在golang中的接口模块
$ go get -u github.com/golang/protobuf/proto

使用protobuf构造golang的模块代码

Protobuf语法

 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
# 创建项目
$ mkdir -p "${GOPATH}/src/protobuf-study"

# 生成protobuf和相应的golang代码
$ mkdir -p "${GOPATH}/src/protobuf-study/protobuf" && pushd "${GOPATH}/src/protobuf-study/goprotobuf"

# 定义protobuf消息格式
sh-4.2# cat test.proto
//这里的语法必须使用proto2,在proto3的版本中和optional参数冲突了
syntax = "proto2";
//显式生命报名,在其他消息格式定义中可以使用package.message的方式来使用类型
//比如goprotobuf.HelloWorld
package goprotobuf;
//声明一个消息体描述一个请求或者响应的消息格式
message HelloWorld {
    required int32     id = 1;
    required string    name = 2;
    optional int32     opt = 3;
}

# 生成对应的golang模块代码(会将protobuf消息格式转换为对应golang结构体)
sh-4.2# protoc --go_out=./ test.proto
sh-4.2# ls
test.pb.go  test.proto

# 看一下生成的test.pd.go的核心代码
// 声明一个消息体描述一个请求或者响应的消息格式
type HelloWorld struct {
	Id               *int32  `protobuf:"varint,1,req,name=id" json:"id,omitempty"`
	Name             *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"`
	Opt              *int32  `protobuf:"varint,3,opt,name=opt" json:"opt,omitempty"`
	XXX_unrecognized []byte  `json:"-"`
}

func (m *HelloWorld) Reset()                    { *m = HelloWorld{} }
func (m *HelloWorld) String() string            { return proto.CompactTextString(m) }
func (*HelloWorld) ProtoMessage()               {}
func (*HelloWorld) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }

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

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

func (m *HelloWorld) GetOpt() int32 {
	if m != nil && m.Opt != nil {
		return *m.Opt
	}
	return 0
}

使用goprotobuf包中定义的protobuf数据格式进行通信

 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
$ pushd $GOPATH/src/protobuf-study

# 一个简单使用protobuf进行读写文件的例子
$ cat write.go
package main
import (
    protobuf "github.com/golang/protobuf/proto"
    "protobuf-study/goprotobuf"
    "fmt"
    "os"
)

func main() {
    //初始化protobuf数据格式
    msg := &goprotobuf.HelloWorld{
        Id:     protobuf.Int32(17),
        Name:   protobuf.String("BGbiao"),
        Opt:    protobuf.Int32(18),

    }

    filename := "./protobuf-test.txt"
    fmt.Printf("使用protobuf创建文件 %s\n",filename)
    fObj,_ := os.Create(filename)
    defer fObj.Close()
    buffer,_ := protobuf.Marshal(msg)
    fObj.Write(buffer)

}

# 测试执行写文件程序
sh-4.2# go run write.go
使用protobuf创建文件 ./protobuf-test.txt
sh-4.2# cat -A protobuf-test.txt
^H^Q^R^FBGbiao^X^R
sh-4.2#

# 一个简单的通过之前定义的protobuf格式进行读取文件内容的例子
sh-4.2# cat read.go
package main
import (
    protobuf "github.com/golang/protobuf/proto"
    "protobuf-study/goprotobuf"
    "fmt"
    "io"
    "os"
)

func checkError(err error) {
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(-1)
    }
}

func main() {
    filename := "protobuf-test.txt"
    file,fileErr := os.Open(filename)
    checkError(fileErr)

    defer file.Close()
    fs,fsErr := file.Stat()
    checkError(fsErr)
    buffer := make([]byte,fs.Size())
    //把file文件内容读取到buffer
    _,readErr := io.ReadFull(file,buffer)
    checkError(readErr)

    //初始化pb结构体对象并将buffer中的文件内容读取到pb结构体中
    msg := &goprotobuf.HelloWorld{}
    pbErr := protobuf.Unmarshal(buffer, msg)
    checkError(pbErr)
    fmt.Printf("读取文件:%s \r\nname:%s\nid:%d\nopt:%d\n",filename,msg.GetName(),msg.GetId(),msg.GetOpt())
}
sh-4.2# go run read.go
读取文件:protobuf-test.txt
name:BGbiao
id:17
opt:18

相关链接

Protobuf语法

知识星球

公众号