我正在构建一个组件,从给定的URL下载信息并将其解析到我的业务类。
这必须分两个阶段进行。正在下载的页面包含一组其他页面的URL,这些页面将在第二阶段下载。
我希望所有这些都尽可能并行,并尝试通过使用TPL Dataflow框架来降低整体复杂性。
这是我的(简化)设置:
我正在重用下载区块,因为我希望通过设置MaxDegreeOfParallelization来以这种方式限制服务器的最大连接次数。
如果我只是使用两个单独的下载块,那么设置会容易得多,但是我不能以这种方式限制连接数,并且仍然拥有尽可能多的并行连接。
现在我的设置存在问题:
如何正确传播完成?当我发布所有URL时,我在缓冲区块上调用 Complete()。但是我不能直接将它传播到下载块,因为即使在缓冲区块已经发布了所有URL之后,仍然可能需要从“Parse Page A”块生成的URL。
但我也将下载块完成连接到缓冲区块和解析页面A块完成,因为解析页面A永远不会完整。
当缓冲区块完成时,我还考虑过调用“Parse Page A”的Complete(),但是下载块中可能仍有数据会被“Parse Block A”拒绝。
是否有摆脱这种循环困境的方法?
还是我完全走错了路,应该以其他方式做到这一点?
答案 0 :(得分:1)
你在逻辑上有一个线性管道,所以我认为你应该在代码中对它进行建模。这意味着为每种类型的页面都有一个单独的下载块。这样,完成工作正常,但您必须单独处理连接限制。
我可以通过两种方式来解决这个问题:
如果您始终连接到同一服务器,则可以使用ServicePoint
s限制与其连接的数量。您可以在程序开头全局设置该限制:
ServicePointManager.DefaultConnectionLimit = limit;
或仅针对一台服务器:
ServicePointManager.FindServicePoint(new Uri("http://myserver.com"))
.ConnectionLimit = limit;
如果使用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();