6

現在、ノンブロッキング SocketChannel (Java 1.6) を使用して、Redis サーバーのクライアントとして機能しています。Redis はソケットを介してプレーンテキスト コマンドを直接受け入れ、CRLF で終了し、同様に応答します。簡単な例を次に示します。

送信: 'PING\r\n'

RECV: '+PONG\r\n'

Redis は、\r\n で終わるデータの多くのセクションをすべて単一の応答の一部として、(要求内容に応じて) 巨大な応答を返すこともできます。

標準のwhile(socket.read() > 0) {//append bytes}ループを使用して、ソケットからバイトを読み取り、クライアント側でそれらを再アセンブルして応答にします。

注: 私はセレクターを使用していません。サーバーに接続された複数のクライアント側 SocketChannel を使用して、送受信コマンドのサービスを待機しています。

私が混乱しているのは、ノンブロッキングモードでのSocketChannel.read()メソッドのコントラクトです。具体的には、サーバーが送信を完了し、メッセージ全体を取得したことを知る方法です。

戻りが速すぎてサーバーに返信する機会を与えるのを防ぐための方法がいくつかありますが、私が行き詰まっていることの1つは次のとおりです。

  1. read()がバイトを返し、その後の呼び出しでバイトを返さずに、別の後続の呼び出しで再びいくつかのバイトを返すことは可能ですか?

基本的に、少なくとも 1 バイトを受信し、最終的にread()が 0 を返す場合、サーバーが応答を完了したことを信頼できますか?それとも、サーバーがビジー状態で、一部を返した可能性がありますか?待って試行し続けると、さらにバイトが増えますか?

read() が 0 バイトを返した後でもバイトを送信し続けることができる場合(以前の読み取りが成功した後)、サーバーが私との通信をいつ終了したかを知る方法がわかりません。スタイル通信は、サーバーがいつ「完了」したかを知ることさえできます。

ご存じのように、接続が停止していて、これらが標準的な長寿命の DB 接続でない限り、read は -1 を返しません。したがって、リクエストごとに閉じたり開いたりすることはありません。

一般的な回答 (少なくともこれらの NIO の質問に対して) は、Grizzly、MINA、または Netty を調べることであることを知っています。可能であれば、サードパーティの依存関係を採用する前に、これらすべてが生の状態でどのように機能するかを本当に知りたいです。

ありがとうございました。

ボーナス質問:

私は当初、ブロッキング SocketChannel がこれを使用する方法になると考えていました。コマンドを処理して応答を返すまで、発信者に何もしてほしくないからです。

それがより良い方法であることが判明した場合、指定されたバッファーを満たすのに十分なバイトがない限り、SocketChannel.read() がブロックされるのを見て少し混乱しました...すべてをバイト単位で読み取ることはできませんこのデフォルトの動作が実際にどのように使用されるのかわかりません...サーバーから返される応答の正確なサイズがわからないため、SocketChannel.read() への呼び出しは常にタイムアウトになるまでブロックされます (その時点で、コンテンツがバッファに残っていることが最終的にわかります)。

ブロッキングメソッドは常に読み取り時にハングアップするため、ブロッキングメソッドを使用する正しい方法についてはよくわかりません。

4

4 に答える 4

4

この答えについては、Redisの仕様を参照してください。

.read()1回の呼び出しで0バイトを返し、後続の呼び出しで1バイト以上を返すという呼び出しの規則に違反しません。これは完全に合法です。ネットワークの遅延またはRedisサーバーの速度低下が原因で配信の遅延が発生した場合、これが発生する可能性があります。

あなたが求める答えは、「Redisサーバーに手動で接続してコマンドを送信した場合、別のコマンドを送信できるように、応答の送信がいつ完了したかをどのように知ることができますか?」という質問に対する同じ答えです。

答えはRedis仕様に記載されている必要があります。コマンドの実行が完了したときにサーバーが送信するグローバルトークンがない場合、これはコマンドごとに実装される可能性があります。Redis仕様でこれが許可されていない場合、これはRedis仕様の障害です。彼らは、すべてのデータをいつ送信したかを知る方法を教えてくれるはずです。これが、シェルにコマンドプロンプトがある理由です。Redisには同等のものが必要です。

Redisの仕様にこれが含まれていない場合は、何らかのタイマー機能を導入することをお勧めします。ソケットを処理するスレッドをコーディングして、5秒間など、指定された期間データが受信されなかった後にコマンドが完了したことを通知します。サーバー上で実行するのにかかる最長のコマンドよりも大幅に長い期間を選択してください。

于 2011-02-07T21:06:48.377 に答える
3

read() が 0 バイトを返した後でもバイトを送信し続けることができる場合 (以前の読み取りが成功した後)、サーバーが私との通信をいつ終了したかを知る方法がわかりません。スタイル通信は、サーバーがいつ「完了」したかを知ることさえできます。

プロトコルを読み、それに従ってください。

http://redis.io/topics/protocol

この仕様では、返信の可能なタイプとそれらを認識する方法について説明しています。一部は行で終了しますが、複数行の応答にはプレフィックス カウントが含まれます。

返信

Redis は、さまざまな種類の応答でコマンドに応答します。サーバーから送信された最初のバイトから、応答の種類を確認できます。

  • 1 行の返信では、返信の最初のバイトは「+」になります。
  • エラーメッセージの場合、応答の最初のバイトは「-」になります
  • 整数の場合、応答の最初のバイトは「:」になります。
  • 一括返信では、返信の最初のバイトは「$」になります
  • マルチバルク返信では、返信の最初のバイトは「*」になります

一行返信

1 行の返信は、"+" で始まり "\r\n" で終わる1 行の文字列の形式です。...

...

複数一括返信

LRANGE のようなコマンドは、複数の値を返す必要があります (リストのすべての要素は値であり、LRANGE は複数の要素を返す必要があります)。これは、複数の一括書き込みを使用して実現され、その後に続く一括書き込みの数を示す最初の行が前に付けられます


read() がバイトを返し、その後の呼び出しでバイトを返さずに、別の後続の呼び出しで再びバイトを返すことは可能ですか? 基本的に、少なくとも 1 バイトを受信し、最終的に read() が 0 を返す場合、サーバーが応答を完了したことを信頼できますか?それとも、サーバーがビジー状態で、一部を返した可能性がありますか?待って試行し続けると、さらにバイトが増えますか?

はい、可能です。サーバーがビジーであるだけでなく、ネットワークの輻輳やルートのダウンにより、データが「一時停止」する可能性があります。データは、アプリケーション プロトコルに関係なく、ストリーム内のどこでも「一時停止」できるストリームです。

ストリームをバッファに読み込み続けます。最初の文字を見て、期待する応答のタイプを判断します。読み取りが成功するたびに、仕様に従って完全なメッセージがバッファーに含まれるまで、バッファーを調べます。


私は当初、ブロッキング SocketChannel がこれを使用する方法になると考えていました。コマンドを処理して応答を返すまで、発信者に何もしてほしくないからです。

私はあなたが正しいと思います。仕様をざっと見てみると、読み取りのブロックはこのプロトコルでは機能しません。行ベースに見えるのでBufferedReaderが役立つかもしれませんが、応答がいつ完了したかを認識する方法を知る必要があります。

于 2011-02-07T21:30:25.837 に答える
2

標準の while(socket.read() > 0) {//append bytes} ループを使用しています

これは、NIO の標準的な手法ではありません。読み取りの結果を変数に格納し、次のことをテストする必要があります。

  1. -1 は EOS を示し、チャネルを閉じる必要があることを意味します
  2. ゼロは、読み取るデータがないことを意味し、select() ループに戻る必要があることを意味します。
  3. 正の値は、そのバイト数を読み取ったことを意味します。続行する前に、ByteBuffer (get()/compact()) から抽出して削除する必要があります。
于 2011-04-15T06:42:46.437 に答える