0

Netty バージョン: 4.0.10.Final

Netty を使用してクライアントとサーバーを作成しました。クライアントとサーバーが行うことは次のとおりです。

サーバ:

  1. クライアントからの接続を待つ
  2. クライアントからメッセージを受け取る
  3. メッセージが悪い場合は、エラー メッセージ (6 バイト) を書き込み、それをフラッシュし、ソケットを閉じて、ソケット内の未読メッセージを読み込まないようにします。それ以外の場合は、メッセージを読み続けます。良いメッセージには何もしないでください。

クライアント:

  1. サーバーに接続します。
  2. N 個の良いメッセージを書いた後、悪いメッセージを 1 つ書き、M 個の良いメッセージを書き続けます。このプロセスは別のスレッドで行われます。このスレッドは、チャネルがアクティブになった後に開始されます。
  3. サーバーからの応答がある場合は、ログに記録してソケットを閉じます。(サーバーはエラーが発生した場合にのみ応答することに注意してください)

クライアントとサーバーの両方を追跡しました。エラーメッセージを書き込んだ後、サーバーが接続を閉じていることがわかりました。悪いメッセージの後に良いメッセージを書き込んでいるときに、クライアントで壊れたパイプ エラーが発生するようになりました。これは、サーバーが不正なメッセージを検出し、エラー メッセージを返し、ソケットを閉じたためです。接続は、リスナーを使用して書き込み操作が完了した後にのみ閉じられます。クライアントは常にサーバーからエラー メッセージを読み取っていません。クライアントでの前のステップ (2) は、I/O スレッドで実行されます。これにより、K 回の実験で受信したエラー メッセージの割合が非常に低くなりました (<10%)。ステップ (2) を別のスレッドに移動した後、% は (70%) になりました。いずれにせよ、それは正確ではありません。壊れたパイプが原因で書き込みが失敗した場合、netty はチャネルの読み取りをトリガーしますか?

更新 1 : 私はここで尋ねられた質問を明確にして回答しているので、誰もが尋ねられた質問/説明を 1 か所で見つけることができます。「あなたは、リセットを引き起こす悪いメッセージを書き、その後に、すでに通らないことがわかっている良いメッセージを書き、破棄された可能性のある応答を読み取ろうとしています。私には意味がありません。何でも」 - EJP より

-- 現実の世界では、クライアントが事前に知ることができない何らかの理由で、サーバーが何かを悪いものとして扱う可能性があります。簡単にするために、クライアントがサーバーからのリセットを引き起こす悪いメッセージを意図的に送信すると言いました。総メッセージに悪いメッセージがあっても、すべての良いメッセージを送信したいと思います。

私がやっていることは、Apple Push Notification Serviceによって実装されたプロトコルに似ています。

4

2 に答える 2

1

メッセージが悪い場合は、エラー メッセージ (6 バイト) を書き込み、それをフラッシュし、ソケットを閉じて、ソケット内の未読メッセージを読み取らないでください。それ以外の場合は、メッセージを読み続けます。

これにより接続がリセットされ、クライアントからは Unix、Linux などで壊れたパイプとして認識されます。

N 個の良いメッセージを書いた後、悪いメッセージを 1 つ書き、M 個の良いメッセージを書き続けます。

これにより、前述の壊れたパイプ エラーが発生します。

このプロセスは別のスレッドで行われます。

なんで?NIO の要点、つまり Netty の要点は、余分なスレッドを必要としないということです。

エラーメッセージを書き込んだ後、サーバーが接続を閉じていることがわかりました。

それはあなたが言ったことです。

悪いメッセージの後に良いメッセージを書き込んでいるときに、クライアントで壊れたパイプ エラーが発生するようになりました。

私が言ったように。

これは、サーバーが不正なメッセージを検出し、エラー メッセージを返し、ソケットを閉じたためです。

正しい。

クライアントは常にサーバーからエラー メッセージを読み取っていません。

接続がリセットされたため。保留中のデータの配信は、リセット後に停止します。

壊れたパイプが原因で書き込みが失敗した場合、netty はチャネルの読み取りをトリガーしますか?

いいえ、データまたは EOS が到着したときに読み取りをトリガーします

ただし、あなたの奇妙なシステム設計/プロトコルは、不可能ではないにしても、それを予測不可能にしています. リセットの原因となる悪いメッセージを書き、その後に、既に通過しないことがわかっている良いメッセージが続き、破棄された可能性のある応答を読み取ろうとしています。私にはまったく意味がありません。ここで何を証明しようとしていますか?

他のみんなと同じように、リクエスト/レスポンス プロトコルを試してください。

于 2013-10-14T23:12:37.903 に答える
0

APN プロトコルは、通知の正常な受信を確認しないため、非常に扱いにくいようです。代わりに、エラーが発生したときに正常に受信した通知を通知するだけです。このプロトコルは、通常、適切な形式の通知を送信することを前提として機能しています。

ある種の有効期限が切れるキャッシュ (ここでは LinkedHashMap が機能する可能性があります) が必要であり、通知で不透明な識別子フィールドをグローバルに一意の順序付けられた値として使用する必要があることをお勧めします。シーケンス番号は機能します (ただし、クライアントを再起動できる場合は保持する必要があります)。

APN を生成するたびに

  • その識別子を次のシーケンス番号に設定します
  • それを送る
  • 現在の時刻と連結されたシーケンス番号の文字列キーを使用して LinkedHashMap に配置します (例: String key = sequenceNumber + "-" + System.currentTimeMillis() )

エラーが発生した場合は、接続を再度開き、マップ内のすべての APN を、エラーで報告された識別子よりも大きいシーケンス番号で再送信する必要があります。これは比較的簡単です。報告されたものよりも小さいシーケンス番号を持つ APN を削除するマップを反復処理するだけです。次に、残りの APN を順番に再送信し、マップ内のそれらを現在の時刻に置き換えます (つまり、再送信するときに APN を削除し、新しい現在の時刻でマップに再挿入します)。

古いエントリのマップを定期的に消去する必要があります。不正な APN を送信した場合に APN サービスがエラーを返すまでにかかる時間に基づいて、妥当な時間を判断する必要があります。数秒で済むと思います(それほど速くはないにしても)。たとえば、1 秒あたり 10 個の APN を送信していて、APN サーバーが 30 秒以内に確実に応答することがわかっている場合、1 秒ごとにパージする 30 秒の有効期限が適切かもしれません。System.currentTimeMillis() - 30000 (30 秒の有効期限) より小さいキーの時間セクションを持つ要素を削除して、マップに沿って反復するだけです。スレッドを適切に同期する必要があります。

書き込みによって発生した IOExceptions をキャッチし、マップに書き込もうとしていた APN を配置して再送信します。

対処できないのは、APN サービスが通知 (または一連の通知) を受信したかどうかがわからない、本物のネットワーク エラーです。影響を受ける APN をすぐに再送信するか、一定期間後に再送信するか、まったく再送信しないかについては、サービスの内容に基づいて決定する必要があります。一定期間後に送信する場合は、送信した時点で新しいシーケンス番号を付与する必要があります。これにより、その間に新しい APN を送信できます。

于 2013-10-16T17:38:52.660 に答える