9

私はかつて .NET で Crawler を作成しました。スケーラビリティを向上させるために、.NET の非同期 API を利用してみました。

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

クローラーがストレステストに行くまで、すべてが完璧に見えます。ストレス テストでは、クローラーのメモリ使用量が高くなります。WinDbg+SoS でメモリをチェックしたところ、多くのバイト配列が System.Threading.OverlappedData インスタンスによって固定されていることがわかりました。インターネットで検索した後、Microsoft からこの KB http://support.microsoft.com/kb/947862を見つけました。

KB によると、非同期 I/O の数には「上限」があるはずですが、「推奨される」境界値はわかりません。したがって、私の目には、この KB は何の役にも立ちません。これは明らかに .NET のバグです。最後に、応答ストリームから非同期でバイトを抽出するという考えを捨てて、同期的な方法でそれを行う必要があります。

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

ネットワーク アプリケーションは、ポストする未処理の非同期 IOの数に上限を設定する必要があります 。

編集:いくつかの疑問符を追加してください。

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

4

5 に答える 5

11

うーん、これは .NET フレームワークの問題ではありません。リンクされた KB 記事は、もう少し明示的だった可能性があります。その銃の弾丸は .NET であり、あえて多くの非同期 I/O 要求を開始する機能を提供します。ある種のリソース制限に達するまで、要求されたとおりに実行します。この場合、おそらく、ジェネレーション 0 ヒープに確保された受信バッファーが多すぎます。

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

于 2008-10-25T14:56:04.483 に答える
3

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

各非同期リクエスト(ファイル、ネットワークなど)がメモリと(少なくともネットワークリクエストの場合は)非ページプールを使用するのは簡単な事実です(アンマネージコードで発生する可能性のある問題の詳細については、こちらを参照してください)。したがって、未処理のリクエストの数は、メモリの量によって制限されます。ビスタ前には、ページングされていないプールの制限が非常に低く、メモリが不足するかなり前に問題が発生していましたが、ビスタ後の環境では、ページングされていないプールを使用する方がはるかに優れています(ここを参照)。

マネージコードでは少し複雑です。アンマネージの世界で発生する問題に加えて、非同期リクエストに使用するメモリバッファが、それらのリクエストが完了するまで固定されるという事実にも対処する必要があるためです。読み取りでこれらの問題が発生しているように聞こえますが、書き込みの場合も同様に悪いです(TCPフロー制御が接続を開始するとすぐに、送信の完了に時間がかかり始めるため、これらのバッファますます長く固定されています-ここここを参照してください)。

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

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

于 2011-05-20T17:16:14.030 に答える
3

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

HTTP/Sockets の .NET 実装は「OK」であるため、ここであなたの質問が何であるかはよくわかりません。いくつかの穴があります (タイムアウトの適切な制御に関する私の投稿を参照してください) が、仕事は完了します (1 秒あたり数百ページをフェッチする本番クローラーがあります)。

ところで、便宜上、同期 IO を使用します。すべてのタスクにはスレッドがあり、同時スレッドの数を制限しています。スレッド管理にはMicrosoft CCRを使用しました。

于 2008-10-25T09:57:51.207 に答える
0

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

あなたがすべきことは、エンベロープ計算の裏側を使用して独自のメトリックを考え出すことです. 1 分間にダウンロードするページ数を計算します。これにより、未解決の非同期リクエストの数 (N) が決まります。

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

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

于 2009-09-14T22:53:53.303 に答える
0

これは、ソケットの async Send (BeginSend) メソッドを使用すると発生します。独自のカスタム スレッドプールを使用し、同期された Send メソッドを使用してスレッド経由でデータを送信すると、この問題はほとんど解決されます。テストされ、証明されました。

于 2011-05-20T10:18:25.020 に答える