【问题标题】:Parallel scraping in .NET.NET 中的并行抓取
【发布时间】:2014-04-09 12:16:48
【问题描述】:
我工作的公司经营着数百个非常活跃的网站。它决定建立一个搜索引擎,而我的任务是编写刮板。一些网站在旧硬件上运行,无法承受太多惩罚,而另一些则可以同时处理大量用户。
我需要能够说对站点 A 使用 5 个并行请求,对站点 B 使用 2 个并行请求,对站点 C 使用 1 个。
我知道我可以使用线程、互斥体、信号量等来完成此操作,但这会非常复杂。任何更高级别的框架(例如 TPL、await/async、TPL Dataflow)是否足够强大,可以以更简单的方式完成此应用程序?
【问题讨论】:
标签:
c#
.net
task-parallel-library
async-await
tpl-dataflow
【解决方案1】:
TPL Dataflow 和 async-await 确实强大且简单,足以满足您的需求:
async Task<IEnumerable<string>> GetAllStringsAsync(IEnumerable<string> urls)
{
var client = new HttpClient();
var bag = new ConcurrentBag<string>();
var block = new ActionBlock<string>(
async url => bag.Add(await client.GetStringAsync(url)),
new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = 5});
foreach (var url in urls)
{
block.Post(url);
}
block.Complete();
await block.Completion;
return bag;
}
【解决方案2】:
我建议您使用HttpClient 和Task.WhenAll,以及SemaphoreSlim 进行简单的节流:
private SemaphoreSlim _mutex = new SemaphoreSlim(5);
private HttpClient _client = new HttpClient();
private async Task<string> DownloadStringAsync(string url)
{
await _mutex.TakeAsync();
try
{
return await _client.GetStringAsync(url);
}
finally
{
_mutex.Release();
}
}
IEnumerable<string> urls = ...;
var data = await Task.WhenAll(urls.Select(url => DownloadStringAsync(url));
或者,您可以使用 TPL 数据流并设置 MaxDegreeOfParallelism 进行限制。