9


9

.NETは信頼できる非同期ソケット通信を持っていませんか?

私は一度Crawlerを.NETで書いた。 そのスケーラビリティを向上させるために、.NETの非同期APIを利用しようとしました。

System.Net.HttpWebRequestには、非同期API BeginGetResponse / EndGetResponseがあります。 ただし、このAPIのペアは、HTTP応答ヘッダーとStreamインスタンスを取得するだけで、そこからHTTP応答コンテンツを抽出できます。 したがって、私の戦略は、BeginGetResponse / EndGetResponseを使用してレスポンスStreamを非同期的に取得し、次にBeginRead / EndReadを使用してレスポンスStreamインスタンスから非同期的にバイトを取得することです。

クローラがストレステストを始めるまで、すべてが完璧だと思われます。 ストレステストの下で、クローラは高いメモリ使用量に苦しんでいます。 私はWinDbg SoSを使ってメモリをチェックし、たくさんのバイト配列がSystem.Threading.OverlappedDataインスタンスによって作成されていることを見つけました。 インターネットで検索した後、私はマイクロソフトからこのナレッジベースhttp://support.microsoft.com/kb/947862を見つけました。

KBによると、非同期I / Oの数には「上限」があるはずですが、「推奨される」上限値はわかりません。 だから、私の目には、このKBは何の助けにもならない。 これは明らかに.NETのバグです。 最後に、レスポンスStreamから非同期にバイトを抽出するという考えをやめて、同期的に行う必要があります。

_ _ ドットネットソケット(Socket.BeginSend / Socket.BeginReceive / NetworkStream.BeginRead / NetworkStream.BeginWrite)で非同期IOを許可する.NETライブラリは、非同期IOで未処理のバッファー(送信または受信)の量に上限が必要です。 。

ネットワークアプリケーションは、投稿する非同期IOの数の上限を設定する必要があります。 _ _

編集:疑問符を追加します。

Socketで非同期I / Oを実行した経験がある人は誰でも 一般的に言って、本番用クローラは同期または非同期でインターネットとのI / Oを行いますか?

5 Answer


11


Hmya、これは.NETフレームワークの問題ではありません。 リンクされたKB記事はもう少し明確になっているかもしれません:「あなたは装填された銃を使っています、これはあなたがそれをあなたの足で狙うときに起こることです」。 その銃の弾丸は.NETで、あなたが望むだけの非同期I / O要求を始めることができます。 ある種のリソース制限に達するまで、それはあなたがそれをするように頼むことをするでしょう。 この場合、おそらく、世代0のヒープ内に固定受信バッファーが多すぎます。

リソース管理は、.NETではなく、依然として私たちの仕事です。 制限なくメモリを割り当てるのと同じです。 この特定の問題を解決するには、未完了のBeginGetResponse()要求の数を制限する必要があります。 何百ものそれらがほとんど意味をなさないようにして、それらの一人一人が一度に1つずつIntertubeを通して絞り込まなければなりません。 別のリクエストを追加しても、完了までに時間がかかるだけです。 またはあなたのプログラムをクラッシュさせる。


3


クローラが同期/非同期の場合でも、明らかに同時リクエスト数を制限します。 その制限は固定されていません、それはあなたのハードウェア、ネットワークに依存します、…​

HTTP / Socketsの.NET実装は「OK」なので、ここであなたの質問が何であるかはよくわかりません。 いくつか問題があります(タイムアウトを適切に制御することについては my postを参照してください)が、仕事は終わります(毎秒数百ページをフェッチするプロダクションクローラーがあります)。

ところで、私たちは同期IOを使いやすくするために使います。 すべてのタスクはスレッドを持っています、そして我々は並行スレッドの数を制限します。 スレッド管理には、 Microsoft CCRを使用しました。


3


これは.Netに限定されません。

各非同期リクエスト(ファイル、ネットワークなど)がメモリと(少なくともある時点でネットワーキングリクエストのために)非ページプールを使用するのは簡単な事実です(http://www.serverframework.com/asynchronousevents/2011/04/を参照)。アンマネージコードで発生する可能性がある問題の詳細については、「非ページプールの枯渇 - 非同期ファイルライターの使用時」を参照してください。 したがって、未処理の要求数はメモリの量によって制限されます。 Vista以前では、メモリ不足になる前に問題が発生する可能性がある非ページプールの制限が非常に低くなっていましたが、Vista以降の環境では、非ページプールの使用にはるかに適しています。 .com / blog / 2009/03 / non-paged-pool.html [こちら])

管理されていない世界で起こる問題に加えて、非同期要求に使用するメモリバッファがそれらの要求が完了するまで固定されているという事実に対処しなければならないため、マネージコードではもう少し複雑です。 読み込みに関してこれらの問題を抱えているように思えますが、書き込みに関してはそれほど悪いことではないにしても同じくらい悪いことです(TCPフロー制御が接続で開始されるとすぐにそれらの送信完了は発生するのに時間がかかり始めるのでより長く固定されている - hereおよびhttp://www.serverframework.com/asynchronousevents/2011を参照/06/tcp-flow-control-and-asynchronous-writes.html[ここ]。

問題は、.NETの非同期のものが壊れているということではなく、抽象化によって、実際よりもはるかに簡単に見えるようになるということです。 たとえば、ピン留めの問題を回避するには、オンデマンドではなくプログラムの起動時に、すべてのバッファを1つの大きな連続ブロックに割り当てます。

個人的には、このようなクローラをアンマネージコードで書くことにしますが、それは私だけです;)あなたはまだ多くの問題に直面するでしょうが、あなたはそれらをもう少し制御することができます。


0


ナレッジベースの記事で上限を示すことはできません。 上限は、利用可能なハードウェアによって異なります。2Gメモリマシンの上限は、16gのRAMを搭載したマシンでは異なります。 GCヒープのサイズ、断片化の程度などによっても異なります。

あなたがすべきことはエンベロープ計算の裏を使ってあなた自身の測定基準を思い付くことです。 1分間にダウンロードするページ数を計算します。 これで、未処理の非同期要求の数が決まります(N)。

Nがわかったら、(プロデューサ - コンシューマパイプラインのコンシューマエンドのような)コードを作成します。これにより、未処理の非同期ダウンロード要求をN個作成できます。 リクエストが終了したら(タイムアウトまたは成功による)、キューからワークアイテムを引っ張って別の非同期リクエストを開始します。

また、ダウンロードが何らかの理由で遅くなった場合など、キューが制限を超えて大きくならないようにする必要があります。


0


これは、ソケットの非同期送信(BeginSend)メソッドを使用したときに発生します。 あなたがあなた自身のカスタムスレッドプールを使用し、同期された送信方法でスレッドを介してデータを送信する場合、ほとんどこの問題を解決しています。 テストして証明した。