Protobufは素晴らしくてダンディですが、自己記述を念頭に置いて作成されたものではありません。これで、明確に定義されたプロトコルを使用していて、たとえば次のように置き換えたい場合は、まったく問題ありません。 SOAPメッセージまたは単にRESTful JSON APIなどを置き換えたい。
しかし、私はそれをWebSocketで使用し、別のURLで何度も閉じて再度開くのではなく、WebSocketのポイントを無効にします-開いたままにして、ワイヤー経由で異なるメッセージを送信します- バイト配列!
今、私の問題は単純ですが、少し複雑です。クライアントからサーバーに(またはその逆に)任意のメッセージを送信したいのですが、受信者は受信したメッセージの種類とその解釈方法を簡単に判断する必要があります。
擬似コードでは、これは次のようになります。
クライアント:
Message m = new Auth().withUserName("Sorona").withPassword("TotallyNotMyActualPassword")
ws.send(m)
サーバー:
Map[Type, Handler]
receiveMessage(Message m) {
handlers.get(m.determineType()).handle(m)
}
ハンドラー:
trait Handler[T] {
def handle()
}
だから私が本当に欲しいのは2つのことです:
1)メッセージサイズを(劇的に)増やすことなく、Anyメッセージの実際のタイプを判別できます。
2)堅牢で拡張可能であること
新しいメッセージタイプの場合は、単にハンドラーを追加して完了します。
維持する必要のあるハードコードされた列挙型、採用する必要のあるスイッチケース構造などはありません。
単純にOOPこれはかなり簡単ですが、Protobufsでは私はちょっと立ち往生しています。
ヒントはありますか?
私がこれまでに思いついたもの:
message Root
{
string type = 1;
google.protobuf.Any content = 2;
}
message Dog
{
string name = 1;
string fav_food = 2;
}
message Cat
{
string name = 1;
uint32 colors = 2;
}
次に、どこか(共有ファイル、データベースなど)で次のようなマップを作成します。
val mapper: Map[String, Class] = Map(
"Dog" -> Dog,
"Cat" -> Cat
)
送信者で私はします:
val nero = Dog().withName("Nero").withFavoriteFood("Raw meat")
val msg = Root().withType("Dog").withMessage(Any.pack(nero))
および受信側:
val r = Root.parseFrom(msg)
val a = r.message.get.unpack(mapper(r.type))
それが基本的な計画です。次に、type
s(「Dog」や「Cat」など)がハードコードされている独自のビルダーを紹介し、クラス/メッセージタイプとハンドラーの間に別のマッパーを追加します。
しかし、私がそこまで来てそれが機能するかどうかは推測します(現在、Scala.jsを使用しているため機能しません。Any.pack
は現在サポートされていないようです)まったく別の話になります;)
ここではさまざまなアプローチを考えることができます。
自己記述型のプロパティを各メッセージに入れたくない場合は、それらを外部で定義し、たとえばメッセージの間に送信することができます。例えば
stream: ... "now comes a dog", { dog }, "now comes a cat", { cat }, ...
「犬がやってくる」をどのようにエンコードするか、そしてそれをメッセージにどのようにマッピングするかはあなた次第です。識別子の構造によっては、自己記述型のメッセージよりも複雑になる場合があります(たとえば、任意の長さの文字列を使用する場合は、その番号を送信する必要もあります)。
カスタムプロトコルは次のようになります
stream: ..., { self-describing dog }, { self-describing cat }, ...
gRPC のようなrpcフレームワークを使用してみることもできます。これにより、より洗練されたデザインが得られます。ここでは、さまざまなメソッドとパラメータを使用してサービスインターフェイスを定義できます。通信が生成され、メソッドの実装を提供するだけで済みます。ただし、WebSocketが完全にサポートされているかどうかはわかりません