ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Protocol Buffer入门

2021-09-18 09:04:06  阅读:148  来源: 互联网

标签:Protocol 入门 err proto Buffer go out os protobuf


1.简介

Protobuf(Google Protocol Buffer)是 Google公司内部的混合语言数据标准,目前已经开源,支持多种语言(C++、C#、Go、JS、Java、Python、PHP),它是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

说简单点,Protobuf就是类似JSON、XML这样的数据交换格式,当今互联网JSON是最流行的格式了,XML用的也挺多,最初接触到Protobuf是因为gRPC默认使用它作为数据编码,相比于JSON和XML,它更小,更快!

举个例子:如果我们想表达一个人名字叫John,年龄是28岁,邮箱是jdoe@gmail.com这样的结构化数据,并且需要在互联网上传输

  • 使用XML表示如下:

    <person>
       <name>John</name>
       <age>28</age>
       <email>jdoe@example.com</email>
     </person>
    
  • 使用JSON表示如下:

    {
        name: John,
        age: 28,
        email: jdoe@example.com
    }
    
  • 使用Protobuf表示如下:

    message Person {
        string name = 1;
        int32 age = 2;
        string email = 3;
    }
    

从可读性和表达能力上看,XML最好,JSON其次,而Protobuf这个其实只是一个DSL,用来定义数据结构和类型,实际生成的数据是二进制的,不可读,但Protobuf追求的是性能和速度,关于它们之间的对比,后面再说,咱们先说用法。

2.安装

#下载源码包
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gz

#解压
tar -xvf protobuf-all-3.6.1.tar.gz
cd protobuf-3.6.1

#编译安装
./configure --prefix=/usr/local/protobuf
make
make check
sudo make install

#配置环境变量
export PATH=$PATH:/usr/local/protobuf/bin/

#配置动态库
cd /etc/ld.so.conf.d/
sudo vi protobuf.conf
添加一行:
/usr/local/protobuf/lib

ldconfig

#确认版本
$ protoc --version
libprotoc 3.6.1

因为protoc不支持将.proto文件编译成golang版本的文件,因此还需要安装protoc-gen-go.protoc-gen-go是protobuf编译插件系列中的Go版本

 go get -u github.com/golang/Protobuf/protoc-gen-go 

安装后,在$gopath/bin目录下会生成protoc-gen-go 的二进制执行文件

3.编写proto文件

示例文件如下(gopath/src/github.com/go-test/protobuf/person.proto):

syntax = "proto3";
option go_package = "go-test/protobuf/g";  //最终生成的go文件会放在这里,注意这是个相对路径,需要结合编译命令来生成最终的文件路径
package protobuf;                   //生成的go文件里的 package的名称

message Person {
    int32 id = 1;
    string name = 2;
    repeated string books = 3;
}

4.编译proto文件

以Go语言为例,在protobuf目录下执行命令:

protoc --go_out=../../ person.proto

其中–go_out表示输出go版本的,其它语言把go替换就行了,比如–php_out、–java_out,不用的版本可能需要下载不同的插件,=后面是需要输出的目录。

执行完命令之后,你会发现当前目录多了一个person.pb.go文件,这是一个标准的go语法文件,里面主要是一个结构体和一些getter函数。

5.使用

以Go为例,我们需要安装一个运行库,其它语言也差不多,官方针对每一个语言都有一个单独的介绍文档,务必查阅一下。

下面是一个完整的案例:

//文件路径:go-test/protobuf/tet.go
package main

import (
	"fmt"
	"github.com/go-test/protobuf/g"
	"google.golang.org/protobuf/proto"
	"io/ioutil"
	"os"
	"encoding/json"
)

func main() {
	//实例化模型对象,填充数据
	p := &g.Person{
		Id:    1,
		Name:  "jun",
		Books:   []string{"math", "english"},
	}

	//Marshal序列化
	out, err := proto.Marshal(p)
	if err != nil {
		panic(err)
	}
	//序列化得到结果是二进制的,是不可读的,所以这里保存到文件
	file, _ := os.OpenFile("out", os.O_CREATE|os.O_WRONLY, 0666)
	_, _ = file.Write(out)
	_ = file.Close()

	//unMarshal还原数据,从文件里面读取
	in, _ := os.Open("out")
	bytes, err := ioutil.ReadAll(in)
	if err != nil {
		panic(err)
	}
	p1 := &g.Person{}
	err = proto.Unmarshal(bytes, p1)
	if err != nil {
		panic(err)
	}
	//调用string()方法打印,也可以使用其生成的getter函数
	fmt.Printf("%s\n", p1.String())
	fmt.Printf("%d\n", p1.GetId)

	//json序列化
	out2, _ := json.Marshal(p)
	file2, _ := os.OpenFile("out2", os.O_CREATE|os.O_WRONLY, 0666)
	_, _ = file2.Write(out2)
	_ = file2.Close()
}

执行后,查看out和out2两个文件的大小

-rw-r--r--  1 root root   22 8月  22 18:05 out
-rw-r--r--  1 root root   48 8月  22 18:05 out2

可以看到protobuf序列化后的文件大小要小于json序列化后的文件。

6.与JSON性能对比

处理速度比较

由于XML目前很少使用在Web API接口上,所以这里就不对比了,主要看一下和JSON的对比,包含2个方面:速度和大小。

为了测试,我在proto文件里面又加了一个数据对象,表示一个组里面有多个person对象

message Group {
    repeated Person persons = 1;
}

测试代码如下:

//文件路径:go-test/protobuf/benchmark/protobuf_test.go
package benchmark

import (
	"github.com/go-test/protobuf/g"
	"google.golang.org/protobuf/proto"
	"encoding/json"
	"testing"
)


func BenchmarkProtos(b *testing.B) {
	group := &g.Group{}
	for i := 0; i < 100; i++ {
		p := &g.Person{
			Id:    int32(i),
			Name:  "测试名称",
			Books:   []string{"car1", "car2", "car3", "car4", "car5", "car7", "car6", "car21", "car22",},

		}
		group.Persons = append(group.Persons, p)
	}
	b.ResetTimer()
	b.N = 100
	for i := 0; i < b.N; i++ {
		out, err := proto.Marshal(group)
		if err != nil {
			panic(err)
		}

		g1 := &g.Group{}
		err = proto.Unmarshal(out, g1)
		if err != nil {
			panic(err)
		}
	}
}

func BenchmarkJsons(b *testing.B) {
	group := &g.Group{}
	for i := 0; i < 100; i++ {
		p := &g.Person{
			Id:    int32(i),
			Name:  "测试名称",
			Books:   []string{"car1", "car2", "car3", "car4", "car5", "car7", "car6", "car21", "car22",},
		}
		group.Persons = append(group.Persons, p)
	}
	b.ResetTimer()
	b.N = 100
	for i := 0; i < b.N; i++ {
		out, err := json.Marshal(group)
		if err != nil {
			panic(err)
		}

		g1 := &g.Group{}
		err = json.Unmarshal(out, g1)
		if err != nil {
			panic(err)
		}
	}
}

压测结果如下:

$ go test -bench="s$" -benchmem .
BenchmarkProtos-4   1000           1099680 ns/op           75096 B/op       1610 allocs/op
BenchmarkJsons-4    1000           2556479 ns/op           70685 B/op       1818 allocs/op

可以看到同样是处理1000次,protobuf每次耗时1099680纳秒,而json每次处理耗时2556479纳秒,显然前者要比后者的处理速度(序列化/反序列化)更快。

数据量比较

测试文件:http://zqwlai/go-test/protobuf/test.go

package main

import (
	"fmt"
	"github.com/go-test/protobuf/g"
	"google.golang.org/protobuf/proto"
	"io/ioutil"
	"os"
	"encoding/json"
)

func main() {
	//实例化模型对象,填充数据
	p := &g.Person{
		Id:    1,
		Name:  "jun",
		Books:   []string{"math", "english"},
	}

	//Marshal序列化
	out, err := proto.Marshal(p)
	if err != nil {
		panic(err)
	}
	//序列化得到结果是二进制的,是不可读的,所以这里保存到文件
	file, _ := os.OpenFile("out", os.O_CREATE|os.O_WRONLY, 0666)
	_, _ = file.Write(out)
	_ = file.Close()

	//unMarshal还原数据,从文件里面读取
	in, _ := os.Open("out")
	bytes, err := ioutil.ReadAll(in)
	if err != nil {
		panic(err)
	}
	p1 := &g.Person{}
	err = proto.Unmarshal(bytes, p1)
	if err != nil {
		panic(err)
	}
	//调用string()方法打印,也可以使用其生成的getter函数
	fmt.Printf("%s\n", p1.String())
	fmt.Printf("%d\n", p1.GetId())

	//json序列化
	out2, _ := json.Marshal(p)
	file2, _ := os.OpenFile("out2", os.O_CREATE|os.O_WRONLY, 0666)
	_, _ = file2.Write(out2)
	_ = file2.Close()
}

查看反序列化后生成的文件大小:

-rw-r--r--  1 root root   22 8月  28 11:36 out
-rw-r--r--  1 root root   48 8月  28 11:36 out2

可以看到proto比json传输的数据量更少。

7.总结

Protobuf作为一种新的数据交换编码方式,虽然使用起来麻烦点,但是在性能和大小上面领先很多,可以用来替换json,使用在一些对性能要求高的场景,比如移动端设备通信。除此之外,目前Protobuf主要用在gRPC用作默认数据编码格式。

标签:Protocol,入门,err,proto,Buffer,go,out,os,protobuf
来源: https://www.cnblogs.com/zqwlai/p/15307054.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有