protobuf的使用

littlexiaoshuishui發表於2021-10-21
protobuf和json/xml的區別

Protocol Buffer 和 XML、JSON一樣都是結構資料序列化的工具,但它們的資料格式有比較大的區別:
首先,Protocol Buffer 序列化之後得到的資料不是可讀的字串,而是二進位制流
其次,XML 和 JSON 格式的資料資訊都包含在了序列化之後的資料中,不需要任何其它資訊就能還原序列化之後的資料;但使用 Protocol Buffer 需要事先定義資料的格式(.proto 協議檔案),還原一個序列化之後的資料需要使用到這個定義好的資料格式
最後,在傳輸資料量較大的需求場景下,Protocol Buffer 比 XML、JSON 更小(3到10倍)、更快(20到100倍)、使用 & 維護更簡單;而且 Protocol Buffer 可以跨平臺、跨語音使用。

protobuf使用過程

1.通過 Protocol Buffer 語法描述需要儲存的資料結構,就是我們編寫的.proto檔案

2.過 Protocol Buffer 編譯器編譯 .proto 檔案。將 .proto 檔案 轉換成對應平臺(python、C++、Java)的程式碼檔案

protoc -I=$SRC_DIR --xxx_out=$DST_DIR   $SRC_DIR/addressbook.proto
​
# 引數說明
# 1. $SRC_DIR:指定需要編譯的.proto檔案目錄 (如沒有提供則使用當前目錄)
# 2. --xxx_out:xxx根據需要生成程式碼的型別進行設定
  """
  對於 Java ,xxx =  java ,即 -- java_out
  對於 C++ ,xxx =  cpp ,即 --cpp_out
  """
# 3. $DST_DIR :編譯後程式碼生成的目錄 (通常設定與$SRC_DIR相同)
# 4. 最後的路徑引數:需要編譯的.proto 檔案的具體路徑
# 編譯通過後,Protoco Buffer會根據不同平臺生成對應的程式碼檔案

go使用protobuf

1.下載protobuf的編譯器protoc,github.com/protocolbuffers/protobu...,根據不同系統安裝對應版本,然後把bin目錄寫到環境變數中,執行protoc –version成功說明安裝成功

>libprotoc 3.17.1

2.獲取protobuf的編譯器外掛protoc-gen-go,其他語言使用其他外掛
這個倉庫官方準備廢棄,使用新

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

3.編寫proto檔案,例如hello.proto


syntax = "proto3";
package proto;
option go_package ="./proto";
​
service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string greeting = 1;
}

4.編譯.proto檔案,發現生成greeter.pb.go檔案

 protoc --go_out=. proto/greeter.proto

protoc編譯器原理

protoc編譯器是通過外掛機制實現對不同語言的支援,比如protoc命令出現--xxx_out格式的引數,那麼protoc將首先查詢是否有內建的xxx外掛,如果沒有內建的xxx外掛那麼將繼續查詢當前系統中是否存在protoc-gen-xxx命名的可執行程式,最終通過查詢到的外掛生成程式碼。所以我們使用–go_out,表示使用protoc-gen-go可執行程式。

如果我們新寫了一個protoc-gen-go-my命令,並且註冊外掛netrpc, 則可以使用命令 –go-my_out=plugins=netrpc。

protoc-gen-go

而這個可執行檔案裡面又實現了一層靜態外掛系統,比如protoc-gen-go內建了一個gRPC外掛,使用者可以通過--go_out=plugins=grpc引數來生成gRPC相關程式碼,否則只會針對message生成相關程式碼。

自定義外掛

我們可以模仿protoc-gen-go,自定義一個外掛。

先看看protoc-gen-go原始碼的main函式(舊版本),所以只要往generator註冊自定義的外掛物件即可,不用修改這個main函式

package main
​
import (
  "io/ioutil"
  "os""github.com/golang/protobuf/proto"
  "github.com/golang/protobuf/protoc-gen-go/generator" //我們新寫的plugin會註冊到這裡
)func main() {
  g := generator.New()
  data, err := ioutil.ReadAll(os.Stdin)
  if err != nil {
  g.Error(err, "reading input")
  }
  if err := proto.Unmarshal(data, g.Request); err != nil {
  g.Error(err, "parsing input proto")
  }
  ...
}

自定義plugin物件,plugins/netrpc.go

generator會處理proto檔案,生成generator.FileDescriptor物件儲存裡面的資訊。

package plugins
​
import (
  "github.com/golang/protobuf/protoc-gen-go/generator"
  "google.golang.org/protobuf/types/descriptorpb"
)//自定義protoc-gen-go外掛,通過--go_out=plugins=netrpc 來生成go程式碼type NetrpcPlugin struct {
  *generator.Generator
}func init() {
  //註冊到generator
  generator.RegisterPlugin(new(NetrpcPlugin))
}func (p *NetrpcPlugin) Name() string {
  return "netrpc"
}
func (p *NetrpcPlugin) Init(g *generator.Generator) {
  p.Generator = g
}func (p *NetrpcPlugin) GenerateImports(file *generator.FileDescriptor) {
  if len(file.Service) > 0 {
  p.genImportCode(file)
  }
}func (p *NetrpcPlugin) Generate(file *generator.FileDescriptor) {
  for _, svc := range file.Service { //處理proto檔案定義的service
  p.genServiceCode(svc)
  }
}func (p *NetrpcPlugin) genImportCode(file *generator.FileDescriptor) {
  p.P("// TODO: import code")
}func (p *NetrpcPlugin) genServiceCode(svc *descriptorpb.ServiceDescriptorProto) {
  p.P("// TODO: service code, Name = " + svc.GetName())
}

編譯自定義外掛生成可執行檔案protoc-gen-go-my

go build -o protoc-gen-go-netrpc
cp protoc-gen-go-netrpc /home/csx/go/bin #把生成的可執行檔案複製到gopath/bin目錄

使用自定義的可執行檔案來處理hello.proto

protoc --go-netrpc_out=plugins=netrpc:. hello.proto

使用go-micro外掛編譯protobuf

該命令會在bin目錄下生成protoc-gen-micro(.exe),protoc編譯器利用protoc-gen-micro外掛將.proto檔案轉換為micro程式碼風格檔案

go get github.com/micro/protoc-gen-micro/v2

2.編譯.proto檔案,會生成greeter.pb.go, greeter.pb.mico.go

protoc --proto_path=. --micro_out=. --go_out=. proto/greeter.proto 

grpc-gateway框架編譯protobuf

兩個命令會在$GOPATH/bin目錄下生成protoc-gen-go,protoc-gen-grpc-gateway

$GOPATH預設是cd ~/go

go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway

protoc-gen-go使用新倉庫

github.com/golang/protobuf已經準備作廢

使用新的庫

google.golang.org/protobuf

安裝go外掛

The compiler plugin protoc-gen-go will be installed in $GOBIN, defaulting to $GOPATH/bin

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
protoc --go_out=. hello.proto
grpc使用

新protoc-gen-go命令不再支援plugin引數,需要重新下載protoc-gen-go-grpc命令

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
protoc --go_out=. hello.proto #編譯message得到變數
protoc --go-grpc_out=. hello.proto #編譯其他資料,依賴message變數

需要生成兩個檔案,hello.pb.go, hello_grpc.pb.go. 合併為一條語句

protoc –go_out=. –go-grpc_out=. hello.proto

protobuf3語法

www.cnblogs.com/tohxyblog/p/897476...

本作品採用《CC 協議》,轉載必須註明作者和本文連結
用過哪些工具?為啥用這個工具(速度快,支援高併發...)?底層如何實現的?

相關文章