<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【GoCN酷Go推薦】protobuf生成Go代碼插件gogo/protobuf

          共 7718字,需瀏覽 16分鐘

           ·

          2021-03-26 14:27

          從 JSON 開始

          談到序列化,大家最先想到的可能是 JSON 或者 XML,這兩種序列化協(xié)議都是基于文本的編碼方式進行數(shù)據(jù)傳輸。類似的還有 YAML 等。

          JSON 擁有許多優(yōu)點,使之成為最廣泛使用的序列化協(xié)議之一。如 JSON 協(xié)議簡單,人眼可讀,序列化后十分簡潔且解析速度快。此外,JSON 具備 JavaScript 的先天性支持,被廣泛應(yīng)用于 Web Browser 的應(yīng)用場景中,并且是 Ajax 的事實標準協(xié)議。

          JSON 的適用場景比較多,典型應(yīng)用場景包括:

          • 公司外部之間傳輸數(shù)據(jù)量相對較小,實時性要求相對低的服務(wù)
          • 基于 Web browser 的 Ajax 請求
          • 接口經(jīng)常發(fā)生變化,并對可調(diào)式性要求較高的場景,例如移動 App 與服務(wù)端的通信

          然而,由于 JSON 本身的設(shè)計的一些特點,在一些場景下使用 JSON 仍然不是最優(yōu)解。如:

          • 需要標準的 IDL ,增強參與各方業(yè)務(wù)約束的場景。由于 JSON 協(xié)議往往只能使用文檔的方式來進行約定,這可能會給調(diào)試帶來一些不便與不明確

          • 對性能和簡潔性有較高要求的場景。JSON 在一些語言中的序列化和反序列化需要采用反射機制,所以在性能要求特別高場景下可能不是最優(yōu)解

          • 對于大數(shù)據(jù)量服務(wù)或持久化場景。JSON 進行序列化的額外空間開銷比較大,這也意味著較大的內(nèi)存和磁盤開銷

          對于以上場景, 使用一些基于 IDL ,存儲方案為二進制存儲的序列化方案則更為合適, 如 ProtoBuf、Thrift、avro等。

          IDL: 參與通訊的各方需要對通訊的內(nèi)容需要做相關(guān)的約定。為了建立一個與語言和平臺無關(guān)的約定,這個約定需要采用與具體開發(fā)語言、平臺無關(guān)的語言來進行描述。這種語言被稱為接口描述語言(IDL),采用IDL撰寫的協(xié)議約定稱之為IDL文件。

          什么是 Protobuf

          ProtoBuf 是 Protocol Buffers 的簡稱 ,是 Google 公司開源的一種語言無關(guān)、平臺無關(guān)、可擴展的序列化結(jié)構(gòu)數(shù)據(jù)的方案,它可用于(數(shù)據(jù))通信協(xié)議、數(shù)據(jù)存儲等。

          ProtoBuf 是上述場景中比較適用的序列化方案之一。ProtoBuf 非常靈活,高效,我們可以通過定義 IDL (在這里是proto)文件,然后使用生成的源代碼輕松的在各種數(shù)據(jù)流中使用各種語言進行編寫和讀取結(jié)構(gòu)數(shù)據(jù)。甚至可以更新數(shù)據(jù)結(jié)構(gòu),而不破壞由舊數(shù)據(jù)結(jié)構(gòu)編譯的已部署程序。

          上文提到,同類型的序列化方案還有 Thrift 和 Avro。其中 Thrift 并不僅僅是序列化協(xié)議,他被嵌入到 Thrift 框架中,這導(dǎo)致其很難和其他傳輸層協(xié)議共同使用。Avro 由于沒有成熟的 JS 實現(xiàn),不適合 Web 環(huán)境, 也 導(dǎo)致其使用場景也比較有限。

          目前 gRPC 默認的序列化方式是 ProtoBuf。

          ProtoBuf 包含序列化格式的定義、各種語言的庫以及一個 IDL 編譯器。正常情況下需要我們定義 proto 文件,然后使用IDL 編譯器編譯成需要的語言。

          一個簡單的 proto 例子 

          syntax = "proto3";                // proto 版本,建議使用 proto3
          option go_package = "main/proto"; // 包名聲明符

          message SearchRequestParam { // message 類型
          enum Type { // 枚舉類型
          PC = 0;
          Mobile = 1;
          }
          string query_text = 1; // 字符串類型 | 后面的「1」為數(shù)字標識符,在消息定義中需要唯一
          int32 limit = 3; // 整型
          Type type = 4; // 枚舉類型
          }

          message SearchResultPage {
          repeated string result = 1; // 「repeated」表示字段可以重復(fù)任意多次(包括0次)
          int32 num_results = 2;
          }
          // test.proto

          代碼中的只是一些比較普通的字段定義,還有一些復(fù)雜的一些字段定義,如Oneof、MapReserved等可以參考官方文檔。

          生成 Go 代碼

          .proto 文件中定義好需要處理的結(jié)構(gòu)化數(shù)據(jù)后,可以通過 protoc 工具,將 .proto 文件轉(zhuǎn)換為 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
          # 執(zhí)行如下表示成功:
          $ protoc --version
          libprotoc 3.15.6

          然后安裝一個官方的生成 Golang 代碼的插件 protoc-gen-go

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

          現(xiàn)在在 proto文件所在目錄下執(zhí)行以下命令以生成go文件

          $ protoc --go_out=. test.proto

          protoc 命令還可以使用-I參數(shù)指定搜索 import 的 proto 的文件夾。其他參數(shù)詳情可以參考官方文檔。

          我們可以在目錄下看到一個 test.pb.go 文件。其中主要結(jié)構(gòu)體如下:

          type SearchRequestParam struct {
           state         protoimpl.MessageState
           sizeCache     protoimpl.SizeCache
           unknownFields protoimpl.UnknownFields

           QueryText 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.MessageState
           sizeCache     protoimpl.SizeCache
           unknownFields protoimpl.UnknownFields

           Result     []string `protobuf:"bytes,1,rep,name=result,proto3...."`
           NumResults int32    `protobuf:"varint,2,opt,name=num_results,json=numResults,proto3..."`

          接下來,就可以在項目代碼中直接使用了。

          gogo/protobuf 是什么

          在上文中,我們安裝了一個「生成 Golang 代碼的插件 protoc-gen-go」,這個插件其實是 golang 官方提供的 一個Protobuf api 實現(xiàn)。而我們的主角gogo/protobuf是基于 golang/protobuf 的一個增強版實現(xiàn)。

          gogo 庫基于官方庫開發(fā),增加了很多的功能,包括:

          • 快速的序列化和反序列化
          • 更規(guī)范的Go數(shù)據(jù)結(jié)構(gòu)
          • goprotobuf 兼容
          • 可選擇的產(chǎn)生一些輔助方法,減少使用中的代碼輸入
          • 可以選擇產(chǎn)生測試代碼和 benchmark 代碼
          • 其它序列化格式

          目前很多知名的項目都在使用該庫,如 etcd、k8s、tidb、docker swarmkit 等。

          gogo/protobuf 如何使用

          https://github.com/gogo/protobuf  根目錄下我們可以看到有很多文件夾,其中「protoc-gen」為前綴的為生成代碼的插件,其他「proto」、「protobuf」、「gogoproto」等為庫文件。

          gogo 庫目前有三種生成代碼的方式

          • gofast: 速度優(yōu)先,但此方式不支持其它 gogoprotobuf 的擴展選項。
          $ go get github.com/gogo/protobuf/protoc-gen-gofast
          $ protoc --gofast_out=. myproto.proto
          • gogofast、gogofaster、gogoslick: 更快的速度、會生成更多的代碼。

            $ go get github.com/gogo/protobuf/proto
            $ go get github.com/gogo/protobuf/{binary} //protoc-gen-gogofast、protoc-gen-gogofaster 、protoc-gen-gogoslick 
            $ go get github.com/gogo/protobuf/gogoproto
            $ protoc -I=. -I=$GOPATH/src -I=$GOPATH/src/github.com/gogo/protobuf/protobuf --{binary}_out=. myproto.proto // 這里的{binary}不包含「protoc-gen」前綴
            • gogofast類似gofast,但是會引入 gogoprotobuf 庫。

            • gogofaster類似gogofast,但是不會產(chǎn)生XXX_unrecognized類的指針字段,可以減少垃圾回收時間。

            • gogoslick類似gogofaster,但是會增加一些額外的string、gostringequal method等。

          • 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
            • 可以通過擴展選項高度定制序列化。

          gogo/protobuf 提供了非常多的擴展選項,以便在產(chǎn)生代碼的時候進行更多的控制。上文提到的擴展選項這里有一個全面的介紹:extensions,擴展選項里主要包含一些生成快速序列化反序列化代碼的可選項、生成更規(guī)范的Golang 數(shù)據(jù)結(jié)構(gòu)的可選項、goprotobuf 兼容的可選項,一些產(chǎn)生輔助方法的可選項、產(chǎn)生測試代碼和benchmark 的可選項,還可以增加 jsontag 等。

          有同學(xué)對以上多個生成方式的序列化性能做了一些壓測,在一般需求下,性能差距并不是很大,protoc-gen-gofast方式基本可以滿足大多數(shù)場景。

          最后,生成的 go 語言代碼在項目中使用就非常簡單了,一般只需要使用proto.Marshal,proto.Unmarshal 方法就可以了,下面是一個例子:

          package main

          import (
           "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 data
           fmt.Println(string(data))

           var respData []byte
           var 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

          序列化和反序列化 - 美團技術(shù)團隊 (meituan.com)

          Protobuf 有沒有比 JSON 快 5 倍?-InfoQ

          幾種Go序列化庫的性能比較 | 鳥窩 (colobu.com)

          思考gRPC :為什么是protobuf | 橫云斷嶺的專欄 (hengyunabc.github.io)

          幾種Go序列化庫的性能比較 | 鳥窩 (colobu.com)



          還想了解更多嗎?

          更多請查看:https://github.com/tidwall/gjson

          歡迎加入我們GOLANG中國社區(qū):https://gocn.vip/


          《酷Go推薦》招募:


          各位Gopher同學(xué),最近我們社區(qū)打算推出一個類似GoCN每日新聞的新欄目《酷Go推薦》,主要是每周推薦一個庫或者好的項目,然后寫一點這個庫使用方法或者優(yōu)點之類的,這樣可以真正的幫助到大家能夠?qū)W習(xí)到新的庫,并且知道怎么用。


          大概規(guī)則和每日新聞類似,如果報名人多的話每個人一個月輪到一次,歡迎大家報名!


          點擊 閱讀原文 即刻報名



          瀏覽 160
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲三级片免费观看 | 午夜激情av | 欧美精品国产亚洲日韩字在线观看 | 波多野成人无码精品视频 | 大鸡吧内射网站 |