Protobufデザインパターン


19

JavaベースのサービスのGoogleプロトコルバッファーを評価しています(ただし、言語に依存しないパターンを期待しています)。2つの質問があります。

1つ目は、広く一般的な質問です。

人々が使用するパターンは何ですか?上記のパターンは、クラス編成(たとえば、.protoファイルごとのメッセージ、パッケージ化、および配布)およびメッセージ定義(たとえば、繰り返しフィールドと繰り返しカプセル化されたフィールド*)などに関連しています。

Google Protobufのヘルプページや公開ブログにはこの種の情報はほとんどありませんが、XMLなどの確立されたプロトコルに関する情報は大量にあります。

また、次の2つの異なるパターンに関する具体的な質問もあります。

  1. .protoファイルでメッセージを表し、それらを個別のjarとしてパッケージ化し、サービスの消費者をターゲットに出荷します。これは基本的にはデフォルトのアプローチです。

  2. 同じことを行いますが、少なくともこれらの2つのメソッドをサポートするコントラクトを実装する各メッセージの周囲に、手作りのラッパー(サブクラスではありません!) :

    public V toProtobufMessage() {
        V.Builder builder = V.newBuilder();
        for (Item item : getItemList()) {
            builder.addItem(item);
        }
        return builder.setAmountPayable(getAmountPayable()).
                       setShippingAddress(getShippingAddress()).
                       build();
    }
    
    public static T fromProtobufMessage(V message_) { 
        return new T(message_.getShippingAddress(), 
                     message_.getItemList(),
                     message_.getAmountPayable());
    }
    

私はによって導入複雑さを離れて非表示にすることができますことを、私は(2)を参照の1つの利点はV.newBuilder().addField().build()、そのようにいくつかの意味のあるメソッドを追加しisOpenForTrade()たりisAddressInFreeDeliveryZone()など、私のラッパーに。(2)で見た2番目の利点は、クライアントが不変オブジェクト(ラッパークラスで強制できるもの)を処理することです。

(2)の欠点の1つは、コードを複製し、ラッパークラスを.protoファイルと同期する必要があることです。

誰かが2つのアプローチのいずれかについてより良い技術やさらなる批判を持っていますか?


*繰り返しフィールドをカプセル化するということは、次のようなメッセージを意味します。

message ItemList {
    repeated item = 1;
}

message CustomerInvoice {
    required ShippingAddress address = 1;
    required ItemList = 2;
    required double amountPayable = 3;
}

このようなメッセージの代わりに:

message CustomerInvoice {
    required ShippingAddress address = 1;
    repeated Item item = 2;
    required double amountPayable = 3;
}

私は後者が好きですが、それに対する議論を聞いてうれしいです。


新しいタグを作成するには、さらに12ポイントが必要です。protobufはこの投稿のタグになるはずです。
Apoorv Khurasia

回答:


13

私が働いているところでは、プロトブフの使用を隠すという決定が下されました。.protoアプリケーション間でファイルを配布するのではなく、protobufインターフェースを公開するすべてのアプリケーションが、それと通信できるクライアントライブラリをエクスポートします。

私はこれらのprotobufを公開するアプリケーションの1つだけに取り組んできましたが、その中で、各protobufメッセージはドメイン内のいくつかの概念に対応しています。各コンセプトには、通常のJavaインターフェイスがあります。次に、実装のインスタンスを取得し、適切なメッセージオブジェクトを作成し、インターフェイスの実装のインスタンスを作成するコンバータークラスがあります(実際には、通常、定義された単純な匿名またはローカルクラスコンバーター内)。protobufが生成したメッセージクラスとコンバーターは、アプリケーションとクライアントライブラリの両方で使用されるライブラリを形成します。クライアントライブラリは、接続を設定し、メッセージを送受信するための少量のコードを追加します。

次に、クライアントアプリケーションはクライアントライブラリをインポートし、送信したいインターフェイスの実装を提供します。確かに、両側が後者を行います。

明確にするために、クライアントがパーティの招待を送信し、サーバーがRSVPで応答する要求/応答サイクルがある場合、関連することは次のとおりです。

  • .protoファイルに書き込まれたPartyInvitationメッセージ
  • PartyInvitationMessage クラス、生成者 protoc
  • PartyInvitation 共有ライブラリで定義されたインターフェイス
  • ActualPartyInvitationPartyInvitationクライアントアプリによって定義されたの具体的な実装(実際には呼び出されません!)
  • StubPartyInvitationPartyInvitation共有ライブラリで定義されたの簡単な実装
  • PartyInvitationConverter、これは、変換することができますPartyInvitationPartyInvitationMessage、そしてPartyInvitationMessageStubPartyInvitation
  • .protoファイルに書き込まれたRSVPメッセージ
  • RSVPMessage クラス、生成者 protoc
  • RSVP 共有ライブラリで定義されたインターフェイス
  • ActualRSVPRSVPサーバーアプリで定義された具体的な実装(実際にはそれとも呼ばれません!)
  • StubRSVPRSVP共有ライブラリで定義されたの簡単な実装
  • RSVPConverter、変換することができたRSVPRSVPMessageRSVPMessageしますStubRSVP

実際の実装とスタブ実装を分けているのは、実際の実装が一般にJPAにマッピングされたエンティティクラスであるためです。サーバーはそれらを作成して永続化するか、データベースから照会してから、送信するprotobufレイヤーに渡します。接続の受信側でそれらのクラスのインスタンスを作成することは適切であるとは感じられませんでした。なぜなら、それらは永続コンテキストに結び付けられないからです。さらに、エンティティには、回線を介して送信されるよりも多くのデータが含まれていることが多いため、受信側で無傷のオブジェクトを作成することさえできません。これが正しい動きであると完全に確信しているわけではありません。

実際、protobufを使用することは良いアイデアだとは完全に確信していません。単純な古いRMIとシリアル化にこだわる場合、ほぼ同じ数のオブジェクトを作成する必要はありませんでした。多くの場合、エンティティクラスをシリアル化可能としてマークし、それに取り掛かることができます。

さて、すべてを言って、私はグーグルで働いているコードベースで、モジュール間の通信にprotobufを多用する友人がいます。まったく異なるアプローチを取ります。生成されたメッセージクラスをまったくラップせず、コードに熱心に(ish)渡します。これは、インターフェースを柔軟に保つ簡単な方法であるため、良いことと見なされています。メッセージが進化したときに同期を維持するための足場コードはありません。生成されたクラスはhasFoo()、時間の経過とともに追加されたフィールドの有無を検出するコードを受信するために必要なすべてのメソッドを提供します。ただし、Googleで働く人々は、(a)かなり頭がよく、(b)ちょっと気が狂う傾向があることに留意してください。


ある時点で、JBoss Serializationを標準のシリアル化の多かれ少なかれドロップイン置換として使用することを検討しました。かなり高速でした。ただし、protobufほど高速ではありません。
トムアンダーソン

jackson2を使用したJSONシリアル化も非常に高速です。GBPが嫌いなのは、メインインターフェイスクラスの不必要な複製です。
Apoorv Khurasia

0

アンダーソンの回答を追加するには、メッセージを巧みに別のメッセージにネストしてやり直すことに細かなラインがあります。問題は、各メッセージが舞台裏で新しいクラスを作成し、データのあらゆる種類のアクセサーとハンドラーを作成することです。ただし、データをコピーするか、1つの値を変更するか、メッセージを比較する必要がある場合は、コストがかかります。これらのプロセスは、大量のデータがある場合や時間に縛られている場合、非常に遅く、痛みを伴う場合があります。


2
これは、より多くの接線のコメントのように読み、見回答する方法
ブヨ

1
まあそのない:クラスのすべての文言の最後には問題がある何のドメインが存在しない(ああ、私はCで、私のすべてのものを開発してい++が、このなければならないという蜂ない問題)
マルコBencik
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.