protobuf生成Go代码插件gogo/protobuf
1. 從json開始
談到序列化,大家最先想到的,可能是 JSON 或者 XML,這兩種序列化協議都是基于文本的編碼方式進行數據傳輸。類似的還有 YAML 等。
JSON 擁有許多優點,使之成為最廣泛使用的序列化協議之一。JSON 協議簡單,人眼可讀,序列化后十分簡潔且解析速度快。此外,JSON 具備 JavaScript 的先天性支持,被廣泛應用于 Web Browser 的應用場景中,并且是 Ajax 的事實標準協議。
JSON 的適用場景比較多,典型應用場景包括:
-
公司外部之間傳輸數據量相對較小,實時性要求相對低的服務。
-
基于 Web browser 的 Ajax 請求。
-
接口經常發生變化,并對可調式性要求較高的場景,例如移動 App 與服務端的通信。
然而,由于 JSON 本身設計的一些特點,在一些場景下使用 JSON 仍然不是最優解。如:
-
需要標準的 IDL ,增強參與各方業務約束的場景。由于 JSON 協議往往只能使用文檔的方式來進行約定,這可能會給調試帶來一些不便與不明確。
-
對性能和簡潔性有較高要求的場景。JSON 在一些語言中的序列化和反序列化需要采用反射機制,在性能要求特別高的場景下,可能不是最優解。
-
對于大數據量服務或持久化場景。JSON 進行序列化的額外空間開銷比較大,這也意味著較大的內存和磁盤開銷。
對于以上場景,使用一些基于 IDL、存儲方案為二進制存儲的序列化方案則更為合適, 如 ProtoBuf、Thrift、avro等。
IDL: 參與通訊的各方需要對通訊的內容做相關的約定。為了建立一個與語言和平臺無關的約定,這個約定需要采用與具體開發語言、平臺無關的語言來進行描述。這種語言被稱為接口描述語言(IDL),采用IDL撰寫的協議約定稱之為IDL文件。
2.?什么是 Protobuf
ProtoBuf 是 Protocol Buffers 的簡稱 ,是 Google 公司開源的一種語言無關、平臺無關、可擴展的序列化結構數據的方案,它可用于(數據)通信協議、數據存儲等。
ProtoBuf 是上述場景中比較適用的序列化方案之一。ProtoBuf 非常靈活、高效,我們可以通過定義 IDL (在這里是proto)文件,然后使用“生成的源代碼”輕松地在各種數據流中使用各種語言進行編寫和讀取結構數據。甚至可以更新數據結構,而不破壞由舊數據結構編譯的已部署程序。
上文提到,同類型的序列化方案還有 Thrift 和 Avro。其中 Thrift 并不僅僅是序列化協議,它被嵌入到 Thrift 框架中,這導致其很難和其他傳輸層協議共同使用;Avro 由于沒有成熟的 JS 實現,不適合 Web 環境, 也導致其使用場景也比較有限。
目前,gRPC 默認的序列化方式是 ProtoBuf。
ProtoBuf 包含序列化格式的定義、各種語言的庫以及一個 IDL 編譯器。正常情況下,我們需要定義?proto?文件,然后使用IDL 編譯器編譯成需要的語言。
3.?一個簡單的?proto?例子
syntax = "proto3"; // proto 版本,建議使用 proto3 option go_package = "main/proto"; // 包名聲明符message SearchRequestParam { // message 類型enum Type { // 枚舉類型PC = 0;Mobile = 1;}string query_text = 1; // 字符串類型 | 后面的「1」為數字標識符,在消息定義中需要唯一int32 limit = 3; // 整型Type type = 4; // 枚舉類型 }message SearchResultPage {repeated string result = 1; // 「repeated」表示字段可以重復任意多次(包括0次)int32 num_results = 2; } // test.proto代碼中只是一些比較普通的字段定義,還有一些復雜的一些字段定義,如Oneof、Map、Reserved等可以參考官方文檔。
4. 根據?proto?文件生成 Go 代碼
在?proto?文件中定義好需要處理的結構化數據后,可以通過?protoc?工具,將?.proto?文件轉換為 C、C++、Golang、Java、Python 等多種語言的代碼。我們這里嘗試一下生成 Golang 語言代碼。
首先,需要安裝?protoc?工具。
# 下載安裝包 (Mac) $ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.15.6/protoc-3.15.6-osx-x86_64.zip # 解壓到 /usr/local 目錄下 $ unzip protoc-3.15.6-osx-x86_64.zip -d protoc-3.15.6-osx-x86_64 $ mv protoc-3.5.0-osx-x86_64/bin/protoc /usr/local/bin/protoc # 執行如下表示成功: $ protoc --version libprotoc 3.15.6然后,安裝一個官方生成 Golang 代碼的插件?protoc-gen-go。
$ go get -u github.com/golang/protobuf/protoc-gen-go接著,在?proto文件所在目錄下,執行以下命令以生成go文件:
$ protoc --go_out=. test.protoprotoc?命令還可以使用-I參數指定搜索 import 的?proto?的文件夾。其他參數詳情可以參考官方文檔。
在?proto文件所在目錄下,我們可以看到一個?test.pb.go?文件。其中主要結構體如下:
type SearchRequestParam struct {state protoimpl.MessageStatesizeCache protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsQueryText string `protobuf:"bytes,1,opt,name=query_text..."` Limit int32 `protobuf:"varint,3,opt,name=limit,proto3"...."` Type SearchRequestParam_Type `protobuf:"varint,4,opt,name=type,proto3..."` } type SearchResultPage struct {state protoimpl.MessageStatesizeCache protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsResult []string `protobuf:"bytes,1,rep,name=result,proto3...."`NumResults int32 `protobuf:"varint,2,opt,name=num_results,json=numResults,proto3..."`接下來,就可以在項目代碼中直接使用 .pb.go?類型文件了。
5. gogo/protobuf 是什么
在上文中,我們安裝了一個「生成 Golang 代碼的插件?protoc-gen-go」,這個插件其實是 golang 官方提供的 一個Protobuf api 實現。我們的主角?gogo/protobuf?是基于?golang/protobuf?的一個增強版實現。
gogo?庫基于官方庫開發,增加了很多的功能,包括:
-
快速的序列化和反序列化。
-
更規范的Go數據結構。
-
goprotobuf 兼容。
-
可選擇的產生一些輔助方法,減少使用中的代碼輸入。
-
可以選擇產生測試代碼和 benchmark 代碼。
-
其它序列化格式。
目前,很多知名的項目都在使用該庫,如 etcd、k8s、tidb、docker swarmkit 等。
6. gogo/protobuf 如何使用
在 https://github.com/gogo/protobuf ?根目錄下,我們可以看到有很多文件夾,其中以 「protoc-gen」 為前綴的文件為生成代碼的插件,其他「proto」、「protobuf」、「gogoproto」等為庫文件。
gogo?庫目前有三種生成代碼的方式
-
gofast:速度優先,但此方式不支持其它 gogoprotobuf 的擴展選項。
- gogofast、gogofaster、gogoslick:速度更快,但會生成更多的代碼。
- gogofast類似gofast,但是會引入 gogoprotobuf 庫。
- gogofaster類似gogofast,但是不會產生XXX_unrecognized類的指針字段,可以減少垃圾回收時間。
- gogoslick類似gogofaster,但是會增加一些額外的string、gostring和equal method等。
-
protoc-gen-gogo:速度最快,可定制化的方式最多。
-
可以通過擴展選項高度定制序列化。
gogo/protobuf 提供了非常多的擴展選項,以便在產生代碼的時候進行更多的控制。上文提到的擴展選項這里有一個全面的介紹:extensions,擴展選項里主要包含一些生成快速序列化和反序列化代碼的可選項、生成更規范的Golang 數據結構的可選項、goprotobuf 兼容的可選項,一些產生輔助方法的可選項、產生測試代碼和benchmark 的可選項,還可以增加 jsontag 等。
有同學對以上多個生成方式的序列化性能做了一些壓測,在一般需求下,性能差距并不是很大,protoc-gen-gofast方式基本可以滿足大多數場景。
最后,生成的 go 語言代碼在項目中使用就非常簡單了,一般只需要使用proto.Marshal、proto.Unmarshal?方法就可以了,下面是一個例子:
package mainimport ("fmt""log"zaproto "git.xxxxx.com/data/za-proto/proto""github.com/gogo/protobuf/proto" )func main() {req := &zaproto.SearchRequestParam{QueryText: "xxxxxx",Limit: 10,Type: zaproto.SearchRequestParam_PC,}data, err := proto.Marshal(req)if err != nil {log.Fatal("Marshal err : err")}// send datafmt.Println(string(data))var respData []bytevar result = zaproto.SearchResultPage{}if err = proto.Unmarshal(respData, &result); err == nil {fmt.Println(result)} else {log.Fatal("Unmarshal err : err")} }?
參考:
alecthomas/go_serialization_benchmarks: Benchmarks of Go serialization methods (github.com)
So you want to use?GoGo?Protobuf (jbrandhorst.com)
Schema evolution in Avro, Protocol Buffers and Thrift — Martin Kleppmann’s blog
Language Guide ?| Protocol Buffers ?| Google Developers
序列化和反序列化 - 美團技術團隊 (meituan.com)
Protobuf 有沒有比 JSON 快 5 倍?-InfoQ
幾種Go序列化庫的性能比較 | 鳥窩 (colobu.com)
思考gRPC :為什么是protobuf | 橫云斷嶺的專欄 (hengyunabc.github.io)
幾種Go序列化庫的性能比較 | 鳥窩 (colobu.com)
https://github.com/gogo/protobuf
?
總結
以上是生活随笔為你收集整理的protobuf生成Go代码插件gogo/protobuf的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gogo
- 下一篇: 使用sikuli测试web网页实例