【问题标题】:.NET's HttpClient Throttling.NET 的 HttpClient 节流
【发布时间】:2012-11-17 02:15:23
【问题描述】:

我正在开发一个基于 .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 的方法。

我应该如何处理这种情况?我想将创建的请求数量限制在一定限度内,但不要阻止等待这些请求完成。

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

【问题讨论】:

  • 您如何调用您发布的代码?您是否有在 foreach 循环中使用的 URL 集合或类似的东西?
  • 没错,我有一个 URL 集合,我将它们转换成一个任务集合。每个映射都是使用上面发布的代码执行的。

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


【解决方案1】:

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

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

良好分区的另一个主要好处是,您还可以避免在高峰时段访问服务器并在路由器级别冒 IP 禁令的风险,以防网站不简单地限制您。

【讨论】:

  • 是的,感谢您的提示,但您的回答并未解决我的主要观点。还是谢谢。
【解决方案2】:

您可以考虑启动一组固定的线程。每个线程串行执行客户端网络操作;可能还会在某些点暂停以进行节流。这将为您提供对加载的特定控制;您可以更改限制策略并更改线程数。

【讨论】:

  • 是的,我知道产生我自己的线程池是要走的路。但我一直在寻找涉及使用异步 .net 框架的解决方案。这清楚吗?
  • 您可以保留异步请求的计数器。添加异步网络操作时增加并从完成处理程序中减少(或其他任何名称。我对此有点生疏)。当你的计数器超过 n 时,为了限制你必须以某种方式推迟新的异步请求。你可能有一个后台线程只是为了这个目的。
  • 好点。有什么方法可以在 http 请求完成时得到通知?我的意思是,每当我创建一个新的 HttpClient 时,我都可以轻松地增加这个计数器,但是......我什么时候减少这个计数器?我还没有在 HttpClient 中找到一个在请求完成时会被调用的钩子。
【解决方案3】:

如果您可以使用 .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(每个服务器可以有多少个连接)设置限制。这样,您可以同时启动所有Tasks,但实际上只有有限数量的人会做某事。虽然限制 Tasks 的数量(例如,通过使用 TPL 数据流,正如我上面建议的那样)很可能更有效。

【讨论】:

  • 太棒了。这似乎是我正在寻找的解决方案,但我无法升级到 .NET 4.5。有没有办法将它移植到 .NET 4 应用程序?
  • 据我所知,没有任何方法可以在 .Net 4.0 上运行 TDF。
  • 好的。无论如何,谢谢,但我无法升级到 .NET 4.5。还有其他想法吗?
  • 你读过我回答的第二部分(关于ServicePointManager)吗?这与 TDF 无关(也许我没有说清楚),它适用于 .Net 4.0。
  • 不,我完全忽略了它,对不起。我会试一试。听起来很有趣。
【解决方案4】:

您可以考虑创建一个新的 DelegatingHandler 来放置在 HTTPClient 的请求/响应管道中,该管道可以计算待处理请求的数量。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-19
    • 2013-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多