.NET的HttpClient Throttling

时间:2012-11-29 03:41:49

标签: .net async-ctp throttling dotnet-httpclient

我正在开发一个基于.NET4的应用程序,它必须请求第三方服务器才能从中获取信息。我正在使用HttpClient来发出这些HTTP请求。

我必须在短时间内创建一百或一千个请求。我想将这些请求的创建限制为一个限制(由常量或其他东西定义),以便其他服务器不会收到大量请求。

我已经检查了this link,显示了如何减少随时创建的任务量。

这是我的非工作方法:

// create the factory
var factory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(level));

// use the factory to create a new task that will create the request to the third-party server
var task = factory.StartNew(() => {
    return new HttpClient().GetAsync(url);
}).Unwrap();

当然,这里的问题是,即使创建了当时的一个任务,也会同时创建和处理大量请求,因为它们在另一个调度程序中运行。我找不到将调度程序更改为HttpClient的方法。

我该如何处理这种情况?我想将创建的请求数量限制为某个限制,但不要阻止等待这些请求完成。

这可能吗?有什么想法吗?

4 个答案:

答案 0 :(得分:1)

如果您可以使用.Net 4.5,一种方法是使用TPL Dataflow中的TransformBlock并设置其MaxDegreeOfParallelism。类似的东西:

var block = new TransformBlock<string, byte[]>(
    url => new HttpClient().GetByteArrayAsync(url),
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = level });

foreach (var url in urls)
    block.Post(url);

block.Complete();

var result = new List<byte[]>();

while (await block.OutputAvailableAsync())
    result.Add(block.Receive());

通过ServicePointManager还有另一种方式来看待这个问题。使用该类,您可以在MaxServicePoints(一次可以连接多少台服务器)和DefaultConnectionLimit(每台服务器可以连接多少个连接)上设置限制。这样,您可以在同一时刻启动所有Task,但实际上只有有限数量的Task会执行某些操作。虽然限制{{1}}的数量(例如,通过使用TPL Dataflow,如上所述)将更有效。

答案 1 :(得分:1)

您可以考虑创建一个新的DelegatingHandler,使其位于HTTPClient的请求/响应管道中,该管道可以保留待处理请求数的计数。

通常,单个HTTPClient实例用于处理多个请求。与HttpWebRequest不同,处理HttpClient实例会关闭底层的TCP / IP连接,因此如果要重用连接,则需要重新使用HTTPClient实例。

答案 2 :(得分:0)

首先,您应该考虑根据网站对工作负载进行分区,或者至少公开一个抽象,让您选择如何对网址列表进行分区。例如,一种策略可以是二级域,例如yahoo.com,google.com。

另一件事是,如果您正在进行严重爬行,您可能需要考虑在云上进行。这样,云中的每个节点都可以抓取不同的分区。当你说“短时间”时,你已经为失败做好准备了。你想要达到的目标需要硬数字。

分区效果的另一个主要好处是,您还可以避免在高峰时段点击服务器,并在路由器级别冒着IP禁令的风险,如果该网站不能简单地限制您。

答案 3 :(得分:0)

您可以考虑启动一组固定的线程。每个线程串行完成客户端网络操作;也许在某些时候暂停以节流。这将为您提供对装载的具体控制;您可以更改限制策略并更改线程数。