Golang protobuf 使用教程

【導讀】本文介紹了 go 語言 protobuf 的使用。

一、安裝

1.protoc
protoc 是 protobuf 文件(.proto)的編譯器,可以藉助這個工具把 .proto 文件轉譯成各種編程語言對應的源碼,包含數據類型定義、調用接口等。

通過查看 protoc 的源碼(參見 github 庫)可以知道,protoc 在設計上把 protobuf 和不同的語言解耦了,底層用 c++ 來實現 protobuf 結構的存儲,然後通過插件的形式來生成不同語言的源碼。可以把 protoc 的編譯過程分成簡單的兩個步驟:

源碼中(參見 github 庫)包含的插件有 csharp、java、js、objectivec、php、python、ruby 等多種。

在 https://github.com/google/protobuf/releases 下載 protoc-3.7.0-win64.zip 解壓後,將 bin 文件夾下的 protoc.exe 複製到 GOPATH/bin 下即可(放這個目錄是因爲,我們的 GOROOT 在配置 GoLang 環境的時候,GoLang 已經幫我們設置好了環境變量,如果你要放其他目錄,那麼要自己去設置環境變量)。

2.protoc-gen-go

protoc-gen-go 是 protobuf 編譯插件系列中的 Go 版本。從上一小節知道原生的 protoc 並不包含 Go 版本的插件

由於 protoc-gen-go 是 Go 寫的,所以安裝它變得很簡單,只需要運行 go get -u github.com/golang/protobuf/protoc-gen-go,便可以在 $GOPATH/bin 目錄下發現這個工具。

如果獲取不到的話 可以自己去 gitbub 上面 clone 或是下載 地址:https://github.com/golang/protobuf
記得下載後要複製到github.com/golang這個目錄下,然後進行手動安裝

go install github.com\golang\protobuf\protoc-gen-go

之後會在 bin 文件夾裏面生成 protoc-gen-go.exe,把 protoc-gen-go.exe 也放到 %GOROOT% 一起

  1. 生成
    可以通過下面的命令來使用 protoc-gen-go 了。比如我們新建一個 testCui.proto,然後執行protoc ./testCui.proto --go_out ./就會生成一個 testCui.pb.go 文件。

我們可以修改命令來指定 proto 文件目錄,且可以控制生成 go 文件的位置。進入 cmd 執行 protoc --proto_path = 你 proto 文件的目錄 --go_out = 生成 go 文件的目錄 你的 proto 文件. proto。這樣就可以指定你的 proto 文件就可以放在任意地方了,包括可以直接在項目裏面生成的 go 文件 不過每次都要進入 cmd 執行這這麼麻煩命令也是很煩的,最後我還是寫了 bat 文件

::執行命令不顯示
@echo off

::更改當前目錄爲批處理本身的目錄 
set PATH=%~dp0
echo BAT PATH:%PATH%

::指定protoc路徑
::set PROTOC=%GOPATH%\bin\protoc.exe
::echo PROTOC PATH:%PROTOC%

::指定當前bat目錄下的protos文件夾爲源目錄
set PROTO_PATH=%PATH%protos
echo SOURCE PROTO_PATH:%PROTO_PATH%

::指定當前bat目錄下的輸出目錄
set SERVER_PATH=%PATH%server
echo SERVER_PATH:%SERVER_PATH%
set CLIENT_PATH=%PATH%client
echo CLIENT_PATH:%CLIENT_PATH%

echo "************************************"

if exist %SERVER_PATH% (
    ::echo %SERVER_PATH% server path is exist
    for /f "delims=" %%i in ('dir /b "%SERVER_PATH%\*.go"') do (
        ::echo delete server file: %%i
        del /q %SERVER_PATH%\%%i
    )
) else (
    echo create server path
    md %SERVER_PATH%
)

if exist %CLIENT_PATH% (
    ::echo %CLIENT_PATH% client path is exist
    for /f "delims=" %%i in ('dir /b "%CLIENT_PATH%\*.js"') do (
        ::echo delete client file: %%i
        del /q %CLIENT_PATH%\%%i
    )
) else (
    echo create client path
    md %CLIENT_PATH%
)

for /f "delims=" %%i in ('dir /b "%PROTO_PATH%\*.proto"') do (
    echo proto file:%%i
    protoc --proto_path=./protos/ %%i --go_out %SERVER_PATH% 
    --js_out=import_style=commonjs,binary:%CLIENT_PATH%
)

pause

這裏我把 protoc.exe 和 protoc-gen-go.exe 都複製到 bat 所在目錄了。雖然可以使用 %PROTOC% 指定 protoc 所在目錄,但是 protoc-gen-go 就比較麻煩了,go_out 參數認不出來。

protoc --js_out=import_style=common.js, binary:. my.proto
這裏有兩種形式,一種是 common.js,一種是 closure(google style).common.js 生成的 js 要使用 require 命令導入,closuer.js 生成的 js 要使用 goo.provide 命令來導入。

二、實例

參考 golang 使用 protobuf 的教程 (https://www.cnblogs.com/jkko123/p/7161843.html),但是我生成的 testCui.pb.go 文件有一些 XXX 字段,然後 Phone 那個初始化就不行,但是 Person 可以

type Phone struct {
    Type                 PhoneType `protobuf:"varint,1,opt,`
    Number               string    `protobuf:"bytes,2,opt,`
    XXX_NoUnkeyedLiteral struct{}  `json:"-"`
    XXX_unrecognized     []byte    `json:"-"`
    XXX_sizecache        int32     `json:"-"`
}

type Person struct {
    Id                   int32    `protobuf:"varint,1,opt,`
    Name                 string   `protobuf:"bytes,2,opt,`
    Phones               []*Phone `protobuf:"bytes,3,rep,`
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
}

所以只能改成這樣:

    p1 := &testpb.Person{

        Id: 1,

        Name: "小張",

        Phones: []*testpb.Phone{

            {testpb.PhoneType_HOME, "111111111", struct{}{}, nil, 0},

            {testpb.PhoneType_WORK, "222222222", struct{}{}, nil, 0},
        },
    };

然後就可以編碼解碼了:

    //創建地址簿
    book := &testpb.ContactBook{};
    book.Persons = append(book.Persons, p1);
    book.Persons = append(book.Persons, p2);
    //編碼數據
    data, _ := proto.Marshal(book);
    //把數據寫入文件
    ioutil.WriteFile("./test.txt", data, os.ModePerm);
}

func read() {
    //讀取文件數據
    data, _ := ioutil.ReadFile("./test.txt");
    book := &testpb.ContactBook{};
    //解碼數據
    proto.Unmarshal(data, book);
    for _, v := range book.Persons {
        fmt.Println(v.Id, v.Name);
        for _, vv := range v.Phones {
            fmt.Println(vv.Type, vv.Number);
        }
    }
三、gogoprotobuf

參考 golang 使用 protobuf
在 go 中使用 protobuf,有兩個可選用的包 goprotobuf(go 官方出品)和 gogoprotobuf。gogoprotobuf 完全兼容 google protobuf,它生成的代碼質量和編解碼性能均比 goprotobuf 高一些。

gogoprotobuf 有兩個插件可以使用

在官網上可以看到:
Getting Started
There are several ways to use gogoprotobuf, but for all you need to install go and protoc. After that you can choose:

1.Speed

Install the protoc-gen-gofast binary

go get github.com/gogo/protobuf/protoc-gen-gofast

Use it to generate faster marshaling and unmarshaling go code for your protocol buffers.

protoc --gofast_out=. myproto.proto

This does not allow you to use any of the other gogoprotobuf extensions.

2.More Speed and more generated code

Fields without pointers cause less time in the garbage collector. More code generation results in more convenient methods.

Other binaries are also included:

protoc-gen-gogofast (same as gofast, but imports gogoprotobuf)
protoc-gen-gogofaster (same as gogofast, without XXX_unrecognized, less pointer fields)
protoc-gen-gogoslick (same as gogofaster, but with generated string, gostring and equal methods)

Installing any of these binaries is easy. Simply run:

go get github.com/gogo/protobuf/proto
go get github.com/gogo/protobuf/{binary}
go get github.com/gogo/protobuf/gogoproto
3.Most Speed and most customization

Customizing the fields of the messages to be the fields that you actually want to use removes the need to copy between the structs you use and structs you use to serialize. gogoprotobuf also offers more serialization formats and generation of tests and even more methods.

Please visit the extensions page for more documentation.

Install protoc-gen-gogo:

go get github.com/gogo/protobuf/proto
go get github.com/gogo/protobuf/jsonpb
go get github.com/gogo/protobuf/protoc-gen-gogo
go get github.com/gogo/protobuf/gogoproto
  1. 安裝
    按照上面的說明,我想試試 gogofaster,裏面有描述 without XXX_unrecognized。在第二部分使用官方庫時,生成的這個 XXX_屬性,在初始化時很煩。
    (1)go get -u github.com/gogo/protobuf/proto 這一步特別慢,差點強行關掉了。
    (2)go get -u github.com/gogo/protobuf/protoc-gen-gogofaster
    (3)go get github.com/gogo/protobuf/gogoproto
    然後就能看到 bin 下面多了一個 protoc-gen-gogofaster.exe 了。
    使用protoc .\testCui.proto --gogofaster_out ./導出新的 go 文件,果然那一堆 XXX 不見了,然後之前的 main 文件可以直接運行。當然把 import 中的 "github.com/golang/protobuf/proto" 改成 "github.com/gogo/protobuf/proto" 也是可以的,使用下面的基準測試發現性能差不多啊,目前還不確定兩者的區別。
package test

import (
    //"github.com/golang/protobuf/proto"
    "github.com/gogo/protobuf/proto"
    "fmt"
    "testing"
    "testpb"
)

func BenchmarkPbMarshal(b *testing.B) {
    p1 := &testpb.Person{
        Id:   1,
        Name: "小張",
        Phones: []*testpb.Phone{
            {testpb.PhoneType_HOME, "111111111"},
            {testpb.PhoneType_WORK, "222222222"},
        },
    };
    p2 := &testpb.Person{
        Id:   2,
        Name: "小王",
        Phones: []*testpb.Phone{
            {testpb.PhoneType_HOME, "333333333"},
            {testpb.PhoneType_WORK, "444444444"},
        },
    };

    //創建地址簿
    book := &testpb.ContactBook{};
    book.Persons = append(book.Persons, p1);
    book.Persons = append(book.Persons, p2);
    //fmt.Println("book",*book)

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _, err := proto.Marshal(book);
        if(err != nil){
            fmt.Println("marshal error")
        }
    }
}

轉自:

jianshu.com/p/7e3dcfbc5fd7

Go 開發大全

參與維護一個非常全面的 Go 開源技術資源庫。日常分享 Go, 雲原生、k8s、Docker 和微服務方面的技術文章和行業動態。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/t44fr7wdXE4PML031T_74A