重用TransformBlock以强制执行最大下载连接

时间:2013-11-03 16:41:01

标签: c# .net task-parallel-library tpl-dataflow

我正在构建一个组件,从给定的URL下载信息并将其解析到我的业务类。

这必须分两个阶段进行。正在下载的页面包含一组其他页面的URL,这些页面将在第二阶段下载。

我希望所有这些都尽可能并行,并尝试通过使用TPL Dataflow框架来降低整体复杂性。

这是我的(简化)设置:

enter image description here

  • 我将URL发布到缓冲区块,将其移动到下载区。
  • 在下载区中下载HTML。
  • 下载块具有两个解析块的条件链接,因此页面类型A的html被移动到“Parse Page A”,这是 TransformManyBlock
  • Parse Page A为B类页面生成一组URL。
  • 再次发布到下载区。
  • 最后,条件链接将页面类型B的HTML发布到最后一个块。

我正在重用下载区块,因为我希望通过设置MaxDegreeOfParallelization来以这种方式限制服务器的最大连接次数

如果我只是使用两个单独的下载块,那么设置会容易得多,但是我不能以这种方式限制连接数,并且仍然拥有尽可能多的并行连接。

现在我的设置存在问题:

如何正确传播完成?当我发布所有URL时,我在缓冲区块上调用 Complete()。但是我不能直接将它传播到下载块,因为即使在缓冲区块已经发布了所有URL之后,仍然可能需要从“Parse Page A”块生成的URL。

但我也将下载块完成连接到缓冲区块和解析页面A块完成,因为解析页面A永远不会完整。

当缓冲区块完成时,我还考虑过调用“Parse Page A”的Complete(),但是下载块中​​可能仍有数据会被“Parse Block A”拒绝。

是否有摆脱这种循环困境的方法?

还是我完全走错了路,应该以其他方式做到这一点?

1 个答案:

答案 0 :(得分:1)

你在逻辑上有一个线性管道,所以我认为你应该在代码中对它进行建模。这意味着为每种类型的页面都有一个单独的下载块。这样,完成工作正常,但您必须单独处理连接限制。

我可以通过两种方式来解决这个问题:

  1. 如果您始终连接到同一服务器,则可以使用ServicePoints限制与其连接的数量。您可以在程序开头全局设置该限制:

    ServicePointManager.DefaultConnectionLimit = limit;
    

    或仅针对一台服务器:

    ServicePointManager.FindServicePoint(new Uri("http://myserver.com"))
                       .ConnectionLimit = limit;
    
  2. 如果使用ServicePoint s对你不起作用(因为你没有一台服务器,因为它会影响整个应用程序,......),你可以使用类似的方式手动限制请求SemaphoreSlim。信号量将设置为您所需的限制,并将在两个下载块之间共享。

    每个块的

    MaxDegreeOfParallelism将设置为相同的限制(更高的值不会添加任何内容,更低的值可能效率低),并且它们的代码可能如下所示:

    try
    {
        await semaphore.WaitAsync();
    
        // perform the download
    }
    finally
    {
        semaphore.Release();
    }
    

    如果确实经常需要这种限制,可以创建一个封装此逻辑的辅助类。它的用法可能如下所示:

    var factory = new SharedLimitBlockFactory<Input, Output>(
        limit, input => Download(input));
    var downloadBlock1 = factory.CreateBlock();
    var downloadBlock2 = factory.CreateBlock();