GolangとProtobuf


文書ディレクトリ
  • 1. Protobuf紹介
  • 2. Protobufの特徴
  • 3. Protobuf環境構成
  • 3.1 protobuf基礎ツール
  • をインストールする
  • 3.2プログラミング言語プラグイン
  • をインストール
  • 3.3 Protobufベース使用
  • 4. Protobufプロトコル構文
  • 5. ProtobufとRPC
  • 1.Protobufの紹介
    ProtobufはProtocol Buffersの略称であり、Googleオープンソースのデータ記述言語であり、XMLやjsonなどのデータ記述言語に類似した初期位置にある.Protobufの最も一般的な使用シーンはRPCシステムに用いられる.これはprotobufが軽量で効率的な構造化データ記憶フォーマットであり、シーケンス化が可能であり、データ記憶やRPCデータインタラクティブフォーマットに適しているからである.
    2.Protobufの特徴
    **メリット:**
  • protobufは効率的なバイナリストレージで、占有空間はXMLより3~10倍小さく、解析速度は20~100倍速い
  • コード生成メカニズム
  • マルチプログラミング言語サポート
  • 互換性が良い
  • 欠点:
  • は可読性が悪く、デバッグが相対的に困難である
  • protobufの詳細については、以下を参照してください.
    https://developers.google.com/protocol-buffers
    3.Protobuf環境構成
    3.1 protobufインフラストラクチャのインストール
  • 自己開発システムに基づいてprotobufコンパイラをダウンロード
    ダウンロードアドレスhttps://github.com/protocolbuffers/protobuf/releases
  • 環境変数
  • の構成
  • #   protobuf     
    $ protoc --version
    libprotoc 3.11.0
    #        
    $ protoc --help
    .
    .
    .
     --cpp_out=OUT_DIR           Generate C++ header and source.
      --csharp_out=OUT_DIR        Generate C# source file.
      --java_out=OUT_DIR          Generate Java source file.
      --js_out=OUT_DIR            Generate JavaScript source.
      --objc_out=OUT_DIR          Generate Objective C header and source.
      --php_out=OUT_DIR           Generate PHP source file.
      --python_out=OUT_DIR        Generate Python source file.
      --ruby_out=OUT_DIR          Generate Ruby source file.
      @<filename>                 Read options and filenames from file. If a
                                  relative file path is specified, the file
                                  will be searched in the working directory.
                                  The --proto_path option will not affect how
                                  this argument file is searched. Content of
                                  the file will be expanded in the position of
                                  @<filename> as in the argument list. Note
                                  that shell expansion is not applied to the
                                  content of the file (i.e., you cannot use
                                  quotes, wildcards, escapes, commands, etc.).
                                  Each line corresponds to a single argument,
                                  even if it contains spaces.
    
    
  • が正常にインストールされていることを確認します.
    3.2プログラミング言語プラグインのインストール
    Go言語のコード生成プラグインを選択しました
    コマンドラインで実行
    go get github.com/golang/protobuf/protoc-gen-go 
    

    このコマンドの実行が完了すると、このようなディレクトリが表示されます.
    $GOPATH/src/github.com/golang/protobuf
    

    インストールに成功すると、$GOPATH/binディレクトリの下でprotoc-gen-goの実行可能ファイルが生成されます.
    Go言語のコード生成プラグインをインストールした後、次の手順でテストします.
  • .protoを接尾辞とするprotobufファイルを記述する
    |
    |__example
    |____ demo2.proto
    ファイルはexample/demo 2です.proto
    syntax = "proto3"; //    protobuf3   
    package example;
    
    message Person {
        string name = 1;
        int32 age = 2;
        enum Gender {
            MALE = 0;
            FEMALE = 1;
            UNKNOWN = 2;
        }
        message Other {
            string addr = 1;
            string hobby = 2;
            Gender g = 3;
        }
        Other info = 3;
    
    }
    
    
  • protoc-gen-goを使用してgo言語コードを生成
    tips:次のコマンドが有効でない場合は、protoc-gen-goの実行可能ファイルを$GOROOT/bin/ディレクトリにコピーしてみてください.
     protoc --go_out=. example/demo2.proto
    

    上記のコマンドについて簡単に説明します.--go_outパラメータは、protocコンパイラに対応するprotoc-gen-goツールをロードするように伝え、このツールによって対応するGolangコードを生成する=.は、生成コードが現在のディレクトリに格納されていることを示します.もちろん、他のファイルディレクトリにも指定できます.example/demo2.protocはprotobufファイルのパスです
    実際の使用中にパラメータを柔軟に使用すればよいdemo2.pb.goというファイルが生成されます
    |
    |__example
    |____ demo2.proto
    |____ demo2.pb.go
    この新しいファイルは自動生成go言語コードです.demo2.pb.goのコードの一部を見てみましょう.
  • 開発の過程で.protoファイルを定義した後、このコードはプラグインが自動的に生成し、異なるプログラミング言語の文法には
  • の違いがある.
  • で生成されたコードは、通常、
  • に注目する必要はありません.
  • ビジネス層では、必要に応じて自動生成コードファイルで提供する
  • のみを使用する.
    // Code generated by protoc-gen-go. DO NOT EDIT.
    // source: demo2.proto
    
    package example
    
    import (
    	fmt "fmt"
    	proto "github.com/golang/protobuf/proto"
    	math "math"
    )
    
    // Reference imports to suppress errors if they are not otherwise used.
    var _ = proto.Marshal
    var _ = fmt.Errorf
    var _ = math.Inf
    
    // This is a compile-time assertion to ensure that this generated file
    // is compatible with the proto package it is being compiled against.
    // A compilation error at this line likely means your copy of the
    // proto package needs to be updated.
    const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
    
    type Person_Gender int32
    
    const (
    	Person_MALE    Person_Gender = 0
    	Person_FEMALE  Person_Gender = 1
    	Person_UNKNOWN Person_Gender = 2
    )
    
    var Person_Gender_name = map[int32]string{
    	0: "MALE",
    	1: "FEMALE",
    	2: "UNKNOWN",
    }
    
    var Person_Gender_value = map[string]int32{
    	"MALE":    0,
    	"FEMALE":  1,
    	"UNKNOWN": 2,
    }
    
    func (x Person_Gender) String() string {
    	return proto.EnumName(Person_Gender_name, int32(x))
    }
    
    func (Person_Gender) EnumDescriptor() ([]byte, []int) {
    	return fileDescriptor_80e54830e2bc2dba, []int{0, 0}
    }
    
    type Person struct {
    	Name                 string        `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
    	Age                  int32         `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
    	Info                 *Person_Other `protobuf:"bytes,3,opt,name=info,proto3" json:"info,omitempty"`
    	XXX_NoUnkeyedLiteral struct{}      `json:"-"`
    	XXX_unrecognized     []byte        `json:"-"`
    	XXX_sizecache        int32         `json:"-"`
    }
    
    func (m *Person) Reset()         { *m = Person{} }
    func (m *Person) String() string { return proto.CompactTextString(m) }
    func (*Person) ProtoMessage()    {}
    func (*Person) Descriptor() ([]byte, []int) {
    	return fileDescriptor_80e54830e2bc2dba, []int{0}
    }
    
    func (m *Person) XXX_Unmarshal(b []byte) error {
    	return xxx_messageInfo_Person.Unmarshal(m, b)
    }
    func (m *Person) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    	return xxx_messageInfo_Person.Marshal(b, m, deterministic)
    }
    func (m *Person) XXX_Merge(src proto.Message) {
    	xxx_messageInfo_Person.Merge(m, src)
    }
    func (m *Person) XXX_Size() int {
    	return xxx_messageInfo_Person.Size(m)
    }
    func (m *Person) XXX_DiscardUnknown() {
    	xxx_messageInfo_Person.DiscardUnknown(m)
    }
    
    var xxx_messageInfo_Person proto.InternalMessageInfo
    
    func (m *Person) GetName() string {
    	if m != nil {
    		return m.Name
    	}
    	return ""
    }
    
    func (m *Person) GetAge() int32 {
    	if m != nil {
    		return m.Age
    	}
    	return 0
    }
    
    func (m *Person) GetInfo() *Person_Other {
    	if m != nil {
    		return m.Info
    	}
    	return nil
    }
    
    type Person_Other struct {
    	Addr                 string        `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"`
    	Hobby                string        `protobuf:"bytes,2,opt,name=hobby,proto3" json:"hobby,omitempty"`
    	G                    Person_Gender `protobuf:"varint,3,opt,name=g,proto3,enum=example.Person_Gender" json:"g,omitempty"`
    	XXX_NoUnkeyedLiteral struct{}      `json:"-"`
    	XXX_unrecognized     []byte        `json:"-"`
    	XXX_sizecache        int32         `json:"-"`
    }
    

    3.3 Protobufベースの使用
    |
    |__example
    |____ demo2.proto
    |____ demo2.pb.go
    |__ main.go main.go
    package main
    
    import (
    	"GoNote/chapter10/demo9/example"
    	"fmt"
    	"github.com/golang/protobuf/proto"
    	"log"
    )
    
    func main() {
    	//         Person   
    	//   Person                 
    	d1 := example.Person{
    		Name: "tom",
    		Age:  99,
    		Info: &example.Person_Other{
    			Addr:  "beijing",
    			Hobby: "code",
    			G:     example.Person_MALE,
    		},
    	}
    	//        
    	d1Encode, err := proto.Marshal(&d1)
    	if err != nil {
    		log.Fatal(err.Error())
    	}
    	fmt.Println(d1Encode)
    	//         
    	d1Decode := example.Person{}
    	err = proto.Unmarshal(d1Encode, &d1Decode)
    	if err != nil {
    		log.Fatal(err.Error())
    	}
    	fmt.Println(d1Decode.GetName())
    	fmt.Println(d1Decode.GetAge())
    	fmt.Println(d1Decode.GetInfo().GetG())
    }
    
    
    go run main.go
    [10 3 116 111 109 16 99 26 15 10 7 98 101 105 106 105 110 103 18 4 99 111 100 101]
    tom
    99
    MALE
    

    **Protobuf基本使用公式ドキュメントを参照することを強くお勧めします:**
    ⭐️ https://developers.google.com/protocol-buffers/docs/gotutorial
    4.Protobufプロトコル構文
    作成された.protoファイルは、特定の構文フォーマットで作成されていることに気づきました.
  • Protobufの構文は比較的簡単です
  • Protobufの語現在proto2proto3の2つのバージョン
  • proto2 https://developers.google.com/protocol-buffers/docs/proto
  • proto3 https://developers.google.com/protocol-buffers/docs/proto3

  • プロジェクトに履歴的な負担がない場合は、proto 3の構文を使用して.protoファイル
  • を作成することを強くお勧めします.
    5.ProtobufとRPC
    ProtobufとRPCを組み合わせて簡単なDemoを作ります
    手順は次のとおりです.
  • 作成.protoファイル
  • 自動生成コード.pb.goのコードファイル
  • RPCを記述するサービス端末
  • PRCを記述するクライアント
  • 実行テスト
  • ディレクトリ構造は次のとおりです.
    |
    |___example
    |______demo3.proto
    |______demo3.pb.go
    |___main
    |______demo3_server.go
    |______demo3_client.go
    
    demo3.protoファイルの作成
    syntax = "proto3"; //    protobuf3   
    package example;
    
    message Demo3Request {
        int64 id = 1;
    }
    message Demo3Response {
        string name = 1;
        int32 age = 2;
        enum Gender {
            MALE = 0;
            FEMALE = 1;
            UNKNOWN = 2;
        }
        message Other {
            string addr = 1;
            string hobby = 2;
            Gender g = 3;
        }
        Other info = 3;
    }
    
    .pb.goファイルの生成
    protoc --go_out=. demo3.proto
    

    rpcサービス側ファイルdemo3_server.goの作成
    package main
    
    import (
    	"GoNote/chapter10/demo9/example"
    	"github.com/pkg/errors"
    	"log"
    	"net/http"
    	"net/rpc"
    )
    
    type Demo3Service struct {
    }
    
    func (d *Demo3Service) GetUser(request example.Demo3Request, response *example.Demo3Response) error {
    	//     
    	//           
    	datas := map[int64]example.Demo3Response{
    		1: {Name: "AAA", Age: 999, Info: &example.Demo3Response_Other{Addr: "beijing", Hobby: "sport", G: example.Demo3Response_MALE}},
    		2: {Name: "BBB", Age: 888, Info: &example.Demo3Response_Other{Addr: "  ", Hobby: "sport", G: example.Demo3Response_FEMALE}},
    		3: {Name: "CCC", Age: 777, Info: &example.Demo3Response_Other{Addr: "wuhan", Hobby: "sport", G: example.Demo3Response_UNKNOWN}},
    		4: {Name: "DDD", Age: 666, Info: &example.Demo3Response_Other{Addr: "  ", Hobby: "sport", G: example.Demo3Response_MALE}},
    		5: {Name: "EEE", Age: 555, Info: &example.Demo3Response_Other{Addr: "", Hobby: "sport", G: example.Demo3Response_FEMALE}},
    	}
    	//         
    	if value, ok := datas[request.GetId()]; ok {
    		*response = value
    	} else {
    		return errors.New("not found")
    	}
    	return nil
    }
    func main() {
    	rpc.Register(new(Demo3Service))
    	rpc.HandleHTTP()
    	err := http.ListenAndServe(":8080", nil)
    	if err != nil {
    		log.Println(err.Error())
    	}
    }
    
    

    rpcクライアントファイルdemo3_client.goの作成
    package main
    
    import (
    	"GoNote/chapter10/demo9/example"
    	"fmt"
    	"log"
    	"net/rpc"
    )
    
    func main() {
    	client, err := rpc.DialHTTP("tcp", ":8080")
    	if err != nil {
    		log.Fatal(err.Error())
    	}
    	request := example.Demo3Request{Id: 1}
    	var response example.Demo3Response
    	err = client.Call("Demo3Service.GetUser", request, &response)
    	if err != nil {
    		log.Fatal(err.Error())
    	}
    	fmt.Println(response.GetName())
    	fmt.Println(response.GetInfo().GetAddr())
    }
    
    

    運転demo3_server.goおよびdemo3_client.go
    AAA
    beijing