0. 序文
Protobuf は、Google が開発したバイナリ シリアル化データ形式で、異なるコンピュータ システム間で構造化データを転送するために使用できます。JSON、XML など、他にも多くのデータ交換形式がありますが、Protobuf にはそれらに比べてどのような利点があるのでしょうか? 主に以下の点に反映されます。
- 高速なシリアル化と逆シリアル化: Protobuf はバイナリ形式であるため、JSON や XML などの他のデータ交換形式よりも高速にメッセージをシリアル化および逆シリアル化できます。これにより、Protobuf を使用すると、高い同時実行性や大規模なデータ処理などのシナリオにおけるアプリケーションのパフォーマンスと効率が大幅に向上します。
- メッセージ サイズの縮小: Protobuf はコンパクトなバイナリ形式であるため、生成するメッセージ サイズが小さくなり、ネットワークの送信や保存に非常に役立ちます。これにより、帯域幅とストレージ容量が節約されると同時に、アプリケーションのパフォーマンスも向上します。
- 互換性とバージョン管理: Protobuf を使用すると、互換性とバージョン管理が簡単になります。メッセージ定義の変更とバージョン管理による簡単な下位互換性と上位互換性により、長い開発サイクルにわたってコードの安定性と信頼性を維持するのに非常に役立ちます。
- 簡略化されたコード: Protobuf を使用すると、コードがより簡潔になり、保守が容易になります。Protobuf は構造体やその他のデータ構造のコードを自動的に生成できるため、複雑なデータ解析やシリアル化コードを手動で記述する必要がなく、コードがより明確で読みやすくなります。
この記事では、インストール、Protobuf メッセージの定義方法、Go コードの生成、メッセージのシリアル化と逆シリアル化、ネットワークでの Protobuf の使用、バージョン管理と互換性など、Goで Protobufを使用する方法について説明します。
1.インストール
プロトコル バッファー (protobuf) のインストールは、次の 2 つの部分に分けることができます。
- プロトックコンパイラをインストールする
- Go の protobuf ライブラリをインストールする
1. Protocコンパイラをインストールする
protoc コンパイラーは、.proto ファイルを C++、Java、Python、Go などの複数のプログラミング言語のコードにコンパイルでき、protobuf のコア コンポーネントです。
最新のコンパイラ リリースは、Protocol Buffers の GitHub: https://github.com/protocolbuffers/protobuf/releases で見つけることができます。
ダウンロード ページで適切なシステム バージョンのダウンロードを見つけて、bin ディレクトリを環境変数に追加します。
以下では、例として Windows x64 を使用します。
- ダウンロード:ここをクリックしてパッケージをダウンロードし、ダウンロードした圧縮パッケージを特定のフォルダーに解凍します。
- **環境変数:** 環境変数を設定している場所を開き、圧縮パッケージ内のbinフォルダーを環境
PATH
変数に追加します。 - **テスト:** cmd を開き、 と入力し
protoc --version
、バージョンが出力されれば、インストールは成功です。
2. Go の protobuf ライブラリをインストールする
次に、Go の protobuf ライブラリをインストールする必要があります。このライブラリは、protoc コンパイラーを使用して GO コードを生成でき、次のコマンドでインストールできます。
go get -u github.com/golang/protobuf/protoc-gen-go
protoc-gen-go は$GOPATH/bin
ディレクトリ、このディレクトリを環境変数に追加する必要があることに注意してください。
2. Protobuf メッセージ タイプを定義する
2.1 .protoパッケージファイルの書き込み
メッセージ タイプは.proto
パッケージで定義されており、ここでメッセージ タイプを作成しperson.proto 文件
、次のような典型的な書き込みメソッドの例を記述します。
syntax = "proto3";
option go_package = "/person";
package example;
message Person {
string name = 1;
int32 age = 2;
repeated string hobbies = 3;
}
syntax = "proto3"
:.proto
ファイルのバージョンを指定するために使用されます。ここでは Protocol Buffers 3 のバージョンを示します。package example:指定消息类型所在的包名,这里包名是
例`。option go_package = "/person";
生成される Go コードのパッケージ名 (パッケージ名) とインポート パス (インポート パス) を指定するために使用されます。message Person { ... }
:Person
という名前の。string name = 1
: タグ番号 のname
名前1
。int32 age = 2
:age
という名前の。そのタグ番号は です2
。repeated string hobbies = 3
: タグ番号 のhobbies
名前3
。repeated
キーワードは、フィールドが配列型であることを示します。
この.proto
ファイルは、Person
という名前の。これにはname
、 、 、および の3 つのage
フィールドが含まれます。および は一般的な単一値タイプのフィールドであり、文字列配列タイプのフィールドです。このファイルでは、各フィールドに一意のタグ番号があり、バイナリ エンコーディングにおけるこのフィールドの位置とタイプを識別するために使用されます。hobbies
name
age
hobbies
2.2 protoc を使用して GO コードを生成する
このファイルのディレクトリでprotoc --go_out=. *.proto
コマンドを実行して GO コードを生成します。
実行後、このディレクトリに追加の person フォルダーがあり、Go ファイルが含まれていることがわかりますperson.pb.go
。このファイルは、構造体 Person と関連メソッドを内部的に定義します。
type Person struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
Hobbies []string `protobuf:"bytes,3,rep,name=hobbies,proto3" json:"hobbies,omitempty"`
}
構造に加えて、プロトコル バッファー メッセージのエンコード、デコード、および操作のためのインフラストラクチャを提供する多くのメソッドがあり、主なメソッドは次のとおりです。
func (*Person) Reset()
:Person
メッセージを。func (*Person) String() string
:Person
メッセージ。func (*Person) ProtoMessage()
Person
:proto.Message
Protobuf メッセージをシリアル化および逆シリアル化するときに必要なインターフェイスを構造体に実装します。func (*Person) Descriptor() ([]byte, []int)
:Person
メッセージ。func (*Person) GetName() string
Person
:Name
メッセージ内のフィールドの値を返します。func (*Person) GetAge() int32
Person
:Age
メッセージ内のフィールドの値を返します。
簡単なテストを書いてみましょう!
func main() {
p:=&person.Person{Name: "yzy",Age: 23,Hobbies: []string{"music","sport"}}
fmt.Println("string",p.String())
fmt.Println("the data:",p.Name,p.Age,p.Hobbies)
fmt.Println("-----------")
fmt.Println("reset the person")
fmt.Println("-----------")
p.Reset()
fmt.Println("string",p.String())
fmt.Println("the data:",p.Name,p.Age,p.Hobbies)
}
実行後、get、string、reset がすべて正常に実行されていることがわかります。
2.3 メッセージのシリアル化と逆シリアル化
シリアル化と逆シリアル化の関数はgithub.com/golang/protobuf/proto
パッケージに含まれています。このパッケージは go get で取得したばかりなので、直接使用できます。以下はシリアル化と逆シリアル化の例であり、シリアル化前後のシーケンスを比較します。一貫性のある。
func TestPersonSerialization(t *testing.T) {
// 创建一个 Person 消息实例并设置其字段
p:=&Person{Name: "yzy",Age: 23,Hobbies: []string{"music","sport"}}
// 将消息序列化为二进制格式
data, err := proto.Marshal(p)
if err != nil {
t.Fatal("marshaling error: ", err)
}
// 反序列化消息
p2 := &Person{}
err = proto.Unmarshal(data, p2)
if err != nil {
t.Fatal("unmarshaling error: ", err)
}
// 比较原始消息和反序列化后的消息
if p.String()!=p2.String() {
t.Fatalf("original message %v != unmarshaled message %v", p, p2)
}
}
2.4 フィールドの種類
フィールド タイプのセクションでは、Go Protobuf の簡単なチュートリアルを参照しています。
2.4.1 スカラー型(Scalar)
プロトタイプ | ゴータイプ | 述べる | プロトタイプ | ゴータイプ | 述べる |
---|---|---|---|---|---|
ダブル | float64 | 浮く | float32 | ||
int32 | int32 | int64 | int64 | ||
uint32 | uint32 | uint64 | uint64 | ||
sint32 | int32 | 負の数に適しています | sint64 | int64 | 負の数に適しています |
固定32 | uint32 | 固定長エンコーディング、2^28 を超える値に適しています | 固定64 | uint64 | 固定長エンコーディング、2^56 を超える値に適しています |
固定32 | int32 | 固定長コード | 固定64 | int64 | 固定長コード |
ブール | ブール | 弦 | 弦 | UTF8 エンコーディング、長さは 2^32 を超えません | |
バイト | []バイト | 任意のバイト シーケンス。長さは 2^32 を超えません。 |
スカラー型が割り当てられていない場合、シリアル化されず、解析時にデフォルト値が割り当てられます。
- 文字列: 空の文字列
- バイト: 空のシーケンス
- ブール値:false
- 値のタイプ: 0
2.4.2 列挙¶
列挙型は、事前に定義された値のセットを提供し、そのうちの 1 つを選択するのに適しています。たとえば、性別を列挙型として定義します。
message Student {
string name = 1;
enum Gender {
FEMALE = 0;
MALE = 1;
}
Gender gender = 2;
repeated int32 scores = 3;
}
- 列挙型の最初のオプションの識別子は 0 である必要があり、これは列挙型のデフォルト値でもあります。
allow_alias
Alias (エイリアス) を使用すると、別名と呼ばれる異なる列挙値に同じ識別子を与えることができ、オプションを開く必要があります。
message EnumAllowAlias {
enum Status {
option allow_alias = true;
UNKOWN = 0;
STARTED = 1;
RUNNING = 1;
}
}
2.4.3 他のメッセージタイプの使用
Result
SearchResponse でメッセージ フィールド タイプとして使用される別のメッセージ タイプです。
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
ネストされた書き込みもサポートされています。
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
他のファイルで定義されている場合は、他のメッセージ タイプをインポートして使用できます。
import "myproject/other_protos.proto";
2.4.4 任意の型 (Any)
Any は、組み込み型が .proto で定義されていないことを意味します。
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
2.4.5 そのうちの 1 つ
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
2.4.6 マップ
message MapRequest {
map<string, int32> points = 1;
}
3. まとめ
これまでのところ、サーバー側とクライアント側で同じ .proto ファイルを通じて .go ファイルを生成し、この .go ファイルを通じて送信するためにシリアル化されたバイナリ形式を使用し、データを逆シリアル化することが可能です。従来の json 形式を使用します。
Probuf を使用して RPC インターフェイスを定義することもできます。これについては今後の記事で説明します。