2009-07-18 4 views
2

SocketChannel.write()を使用して大量のデータを送信しているときに、基礎となるTCPバッファがいっぱいになって、write()を続けて再試行する必要があります。データはすべて送信されます。Java NIO SocketChannel.write()でのスレッドの問題

だから、私はこのようなものを持っているかもしれません:

public void send(ByteBuffer bb, SocketChannel sc){ 
    sc.write(bb); 
    while (bb.remaining()>0){ 
     Thread.sleep(10); 
     sc.write(bb);   
    } 
} 

問題は大のByteBufferと溢れ根本的なTCPバッファと時折問題は、この呼び出しが予期しないためにブロックされます)(送信することを意味していることです時間の長さ。私のプロジェクトでは、同時に数百のクライアントが接続されています.1つのソケット接続に起因する1つの遅延は、1つのSocketChannelでこの1つの遅延が解決されるまでシステム全体をクロールさせます。遅延が発生すると、プロジェクトの他の領域での速度低下の連鎖反応が発生し、レイテンシが低いことが重要になります。

このTCPバッファオーバーフローの問題を透過的に処理し、SocketChannel.write()への複数の呼び出しが必要なときにすべてをブロックすることなく解決するソリューションが必要です。私はsend()をスレッドを拡張する別のクラスに入れることを考えました。それは独自のスレッドとして実行され、呼び出しコードをブロックしません。しかし、SocketChannel.write()が最初の試行で成功した場合、特に99%の時間がかかると、スレッドがそこに存在する必要はありません。 。 (つまり、send()を別のスレッドに入れることは、while()ループが使用されている場合にのみ必要です。バッファの問題がある場合(たぶん1%の時間)バッファの問題がある場合時間のわずか1%で、私はsend()の呼び出しの他の99%に対してスレッドのオーバーヘッドを必要としません。

私はそれが理にかなってほしいと思っています...私は本当にいくつかの提案を使用することができます。ありがとう!

答えて

1

Java NIOより前のバージョンでは、にはがあり、パフォーマンスを向上させるためにソケットあたり1つのスレッドを使用しました。これは、Javaだけでなく、すべてのソケットベースのアプリケーションで問題になります。これを克服するために、すべてのオペレーティングシステムに非ブロッキングIOのサポートが追加されました。 Java NIO実装はSelectorsに基づいています。

The definitive Java NIO bookとこのOn Javaの記事を参照してください。ただし、これは複雑なトピックであり、コードにマルチスレッドの問題がいくつか残っています。詳細については、Googleの「非ブロックNIO」を参照してください。

1

書き込みがすぐに戻るかブロックされるため、sleep()は必要ありません。 初めて書き込みをしない場合は、書き込みを渡すエグゼキュータを持つことができます。 もう1つの選択肢は、書き込みを実行するための小さなスレッドプールを用意することです。

しかし、ソケットが別の書き込みを実行する準備ができていることを知るために、セレクタを使用するのが最良の選択肢です(推奨されているように)。

+0

ソケットが完全なバッファでブロックされていない場合、元のポスターは言っていません。 –

+0

バッファは、クリアするために常に少なくとも10ミリ秒かかると仮定します。個人的には、バッファがほとんどすぐにクリアされない場合、バッファが小さすぎるか、リーダーが遅すぎるかのいずれかです。私は接続を閉じます。 –

+0

例のバイトバッファがカーネルのソケット出力バッファより大きい場合、コードはネットワークアダプタが書き込むことができ、したがってオーバーランしてスリープするよりも速く書ける可能性があるため、非効率的です。 10msスリープ状態では、ネットワークアダプタは1Gbネットワークに約1MBを書き込むことができます。通常のソケット出力バッファサイズよりも大きくなります。バイトバッファがほんの数KBであれば、読者が遅すぎるかもしれませんし、OP_WRITEを使用すると少し複雑になりますが、バッファアンダーランだけでなくスレッドをスリープ状態にするのを避けることができます。 –

0

何百もの接続について、おそらくNIOを気にする必要はありません。古い古いソケットとスレッドをブロックすることができます。

NIOを使用すると、選択キーとしてOP_WRITEに関心を登録することができ、さらにデータを書き込む余地があるときに通知を受けることができます。

0

既にループがあると仮定して、いくつかのことを行う必要があります。 Selector.select();どのソケットがI/Oの準備ができているかを判断します。

  • 作成した後にソケットチャネルを非ブロックに設定します。sc.configureBlocking(false);
  • バッファを書き込みます(その一部)。残っているものがあるかどうかを確認します。バッファそのものは、現在の位置と残っている量を処理します。 - あなたは小さなお持ちの場合

何か

sc.write(bb); 
if(sc.remaining() == 0) 
    //we're done with this buffer, remove it from the select set if there's nothing else to send. 
else 
    //do other stuff/return to select loop 
  • ように私は今、同じ問題のいくつかに直面しています
0

眠るあなたのwhileループを取り除きます大量の転送では、私はスレッドプールを作成し、ライタースレッドの書き込みブロックを許可します。
- あなたは接続の多くを持っているなら、あなたは完全なJava NIOを使用して、あなたの受け入れ()されたソケットにOP_WRITEを登録して、セレクタが入ってくるのを待つことができ

OriellyのJava NIOの本はすべて持っています。この。
また: http://www.exampledepot.com/egs/java.nio/NbServer.html?l=rel

いくつかの研究をオンラインでは、着信接続の多くを持っていない限り、NIOはかなり行き過ぎであると信じて私をリードしてきました。さもなければ、ほんの数回の大きな転送であれば、書き込みスレッドを使用してください。それはおそらくより速い応答を持つでしょう。多くの人々が、NIOが必要とするほど迅速に再送信しないという問題を抱えています。あなたの書き込みスレッドは独自のブロックになっているので、あなたを傷つけません。

0

私はセレクトキーにOP_WRITEを登録するとトムは答えます。

私はRox Java NIO Tutorialをお勧めします。

0

私がJava NIOについて読むほど、それは私にその義務を与えます。とにかく、私はこの男がスリープループよりエレガントな解決策を持っているようですね

http://weblogs.java.net/blog/2006/05/30/tricks-and-tips-nio-part-i-why-you-must-handle-opwrite

...この記事はあなたの問題を答えると思います。

また、Java NIOを単独で使用するのは危険です。私ができるところでは、Java NIOとその小さな「驚き」より優れた抽象概念を提供するApache MINAをおそらく使用すると思います。