Netty + ProtoBuffer:1 つの接続に対して少数の通信メッセージ
-
25-10-2019 - |
質問
Netty チュートリアルを読んでいると、簡単な方法を見つけました。 説明 Netty を統合する方法と Google プロトコル バッファー. 。私はその例を調査し始め (ドキュメントにこれ以上の情報がないため)、サンプルの現地時間アプリケーションのような単純なアプリケーションを作成しました。ただし、この例では PipeFactory クラスで静的初期化を使用しています。例:
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.codec.protobuf.ProtobufDecoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufEncoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import static org.jboss.netty.channel.Channels.pipeline;
/**
* @author sergiizagriichuk
*/
class ProtoCommunicationClientPipeFactory implements ChannelPipelineFactory {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline p = pipeline();
p.addLast("frameDecoder", new ProtobufVarint32FrameDecoder());
p.addLast("protobufDecoder", new ProtobufDecoder(Communication.DataMessage.getDefaultInstance()));
p.addLast("frameEncoder", new ProtobufVarint32LengthFieldPrepender());
p.addLast("protobufEncoder", new ProtobufEncoder());
p.addLast("handler", new ProtoCommunicationClientHandler());
return p;
}
}
(ラインを見てください p.addLast("protobufDecoder", new ProtobufDecoder(Communication.DataMessage.getDefaultInstance()));
)そして、(私が理解しているように)1つの工場だけを作成できます ClientBootstrap
クラス、つまり bootstrap.setPipelineFactory()
方法。したがって、この状況では使用できます 1つ サーバーに送信するメッセージと 1つ サーバーからメッセージを受信するのですが、それは私にとって悪いことであり、私だけではないと思います:( 1つの接続で送受信する異なるメッセージを使用するにはどうすればよいですか?もしかしたらいくつか作れるかも知れません protobufDecoder
このような
p.addLast("protobufDecoder", new ProtobufDecoder(Communication.DataMessage.getDefaultInstance()));
p.addLast("protobufDecoder", new ProtobufDecoder(Communication.TestMessage.getDefaultInstance()));
p.addLast("protobufDecoder", new ProtobufDecoder(Communication.SrcMessage.getDefaultInstance()));
それとも他のテクニック?どうもありがとう。
解決
Netty Inの著者のスレッドを見つけました Googleグループ そして、上に書いたようにアーキテクチャを変更したり、自分のデコーダーを書いたりしなければならないことを理解していたので、どんな方法が簡単かつ良くなるかを考え始めてください。
他のヒント
とにかく独自のコーデックを書く場合は、カスタムデータオブジェクトの外部化可能なインターフェイスを実装することを検討することをお勧めします。
- Serializableは低エフォルトですが、最悪のパフォーマンス(すべてをシリアル化)します。
- Protobufは、努力とパフォーマンスの間の良いトレードオフです(プロトメンテナンスが必要です)
- 外部化可能は高い努力ですが、最高のパフォーマンス(カスタムミニマルコーデック)
あなたのプロジェクトが山のヤギのようにスケーリングする必要があることをすでに知っているなら、あなたは困難な道を行く必要があるかもしれません。 Protobufは銀の弾丸ではありません。
理論的には、これは、着信メッセージに合わせて入ってくる各メッセージのパイプラインを変更することで実行できます。を見てください ポート統一 Nettyの例。
シーケンスは次のとおりです。
1)フレームデコーダーまたは別の「deCodermappingDecoder」で、受信メッセージのメッセージタイプを確認します
2)例に示すように、パイプラインを動的に変更する
ただし、別の接続を使用して、このシーケンスに従ってみませんか。
1)着信メッセージに基づいて、パイプラインに他のデコーダーを1回だけ追加します。
2)追加します 同じ チャンネルアップストリームハンドラーのインスタンスパイプラインの最後のハンドラーとして、この方法ですべてのメッセージが同じインスタンスにルーティングされます。これは、単一の接続があるようなものです。
問題は、バイナリ形式で2つの異なるプロトブフメッセージを区別する方法がないことです。しかし、Protobufファイル内でそれを解決する方法があります。
message AnyMessage {
message DataMessage { [...] }
optional DataMessage dataMessage = 1;
message TestMessage { [...] }
optional TestMessage testMessage = 2;
message SrcMessage { [...] }
optional SrcMessage srcMessage = 3;
}
設定されていないオプションのフィールドは、頭上では生成されません。さらに、列挙を追加できますが、それは単なるボーナスです。
この問題は、Netty の制限やエンコーダ/デコーダの制限とはまったく異なります。問題は、Google プロトコル バッファーがオブジェクトをシリアル化/逆シリアル化する方法を提供しているだけで、プロトコルを提供していないことです。標準ディストリビューションの一部として、ある種の RPC 実装が用意されていますが、RPC プロトコルを実装しようとすると、最終的に 3 つの間接層が必要になります。プロジェクトの 1 つで私が行ったことは、基本的にメッセージの結合であるメッセージを定義することでした。このメッセージには、タイプであるフィールドが 1 つと、実際のメッセージであるフィールドが 1 つ含まれています。最終的には間接層は 2 つになりますが、3 つにはなりません。このように、Netty の例はうまく機能しますが、前の投稿で述べたように、ビジネス ロジック ハンドラーにさらにロジックを追加する必要があります。
メッセージトンネリングを使用して、さまざまなタイプのメッセージを1つのメッセージでペイロードとして送信できます。それが役立つことを願っています
長い研究と苦しみの後...私はメッセージの構成を1つのラッパーメッセージに使用するというアイデアを思いつきました。そのメッセージの中で私が使用します の一つ 許可されたオブジェクトの数を制限するキー 唯一。例を確認してください:
message OneMessage {
MessageType messageType = 1;
oneof messageBody {
Event event = 2;
Request request = 3;
Response response = 4;
}
string messageCode = 5; //unique message code
int64 timestamp = 6; //server time
}