我需要向在线api发起Web服务请求,我认为Parallel Extensions非常适合我的需求。
有问题的网络服务旨在重复调用,但如果您每秒超过一定数量的呼叫,则会有一种机制向您收费。我显然希望尽量减少我的收费,所以想知道是否有人见过可以应对以下要求的TaskScheduler:
人们是否觉得这些是任务调度员应该处理的责任,还是我在咆哮错误的树?如果您有其他选择,我愿意接受建议。
答案 0 :(得分:7)
我同意其他人的看法,TPL Dataflow听起来是一个很好的解决方案。
要限制处理,您可以创建一个实际上不会以任何方式转换数据的TransformBlock
,如果它在之前的数据之后过早到达,则会延迟它:
static IPropagatorBlock<T, T> CreateDelayBlock<T>(TimeSpan delay)
{
DateTime lastItem = DateTime.MinValue;
return new TransformBlock<T, T>(
async x =>
{
var waitTime = lastItem + delay - DateTime.UtcNow;
if (waitTime > TimeSpan.Zero)
await Task.Delay(waitTime);
lastItem = DateTime.UtcNow;
return x;
},
new ExecutionDataflowBlockOptions { BoundedCapacity = 1 });
}
然后创建一个生成数据的方法(例如从0开始的整数):
static async Task Producer(ITargetBlock<int> target)
{
int i = 0;
while (await target.SendAsync(i))
i++;
}
它是异步编写的,因此如果目标块现在无法处理项目,它将等待。
然后写一个消费者方法:
static void Consumer(int i)
{
Console.WriteLine(i);
}
最后,将它们连接起来并启动它:
var delayBlock = CreateDelayBlock<int>(TimeSpan.FromMilliseconds(500));
var consumerBlock = new ActionBlock<int>(
(Action<int>)Consumer,
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });
delayBlock.LinkTo(consumerBlock, new DataflowLinkOptions { PropagateCompletion = true });
Task.WaitAll(Producer(delayBlock), consumerBlock.Completion);
此处,delayBlock
每500毫秒最多可接受一个项目,Consumer()
方法可以并行运行多次。要完成处理,请致电delayBlock.Complete()
。
如果你想为你的#2添加一些缓存,你可以创建另一个TransformBlock
在那里做工作并将其链接到其他块。
答案 1 :(得分:3)
老实说,我会在更高的抽象层次上工作,并为此使用TPL Dataflow API。唯一的问题是你需要编写一个自定义块,它会以你需要的速率限制请求,因为默认情况下,块是“贪婪的”,并且只会尽可能快地处理。实现将是这样的:
BufferBlock<T>
开头,这是您要发布的逻辑块。 BufferBlock<T>
链接到具有请求/秒和限制逻辑知识的自定义块。ActionBlock<T>
。我没有时间在第二个时间内为#2编写自定义块,但我会稍后再回来查看,如果你还没有弄明白的话,请尝试为你填写一个实现。
答案 2 :(得分:2)
我没有多少使用过RX,但AFAICT Observable.Window方法可以正常使用。
http://msdn.microsoft.com/en-us/library/system.reactive.linq.observable.window(VS.103).aspx
它似乎比Throttle更合适,似乎扔掉了元素,我猜这不是你想要的
答案 3 :(得分:0)
如果您需要按时间限制,则应该查看Quartz.net。它可以促进一致的轮询。如果您关心所有请求,则应考虑使用某种排队机制。 MSMQ可能是正确的解决方案,但如果您想要扩大规模并使用NServiceBus或RabbitMQ等ESB,则有许多具体的实施方案。
更新
在这种情况下,如果您可以利用CTP,TPL Dataflow是您首选的解决方案。一个受限制的BufferBlock是解决方案。
此示例来自documentation provided by Microsoft:
// Hand-off through a bounded BufferBlock<T>
private static BufferBlock<int> m_buffer = new BufferBlock<int>(
new DataflowBlockOptions { BoundedCapacity = 10 });
// Producer
private static async void Producer()
{
while(true)
{
await m_buffer.SendAsync(Produce());
}
}
// Consumer
private static async Task Consumer()
{
while(true)
{
Process(await m_buffer.ReceiveAsync());
}
}
// Start the Producer and Consumer
private static async Task Run()
{
await Task.WhenAll(Producer(), Consumer());
}
更新
查看RX的Observable.Throttle。