我正在编写一个客户端向Web服务(CQRS)发出命令的应用程序
我的问题是,有时客户端只是发出太多请求,服务开始返回它太忙了。
这里是一个例子。我正在注册订单,订单数量可能从几千到几千。
var taskList = Orders.Select(order => _cmdSvc.ExecuteAsync(order))
.ToList();
await Task.WhenAll(taskList);
基本上,我为每个订单调用ExecuteAsync并取回一个Task。然后我只等他们完成。
我真的不想修复此服务器端,因为无论我对其进行多大调整,客户端仍然可以通过发送10,000个请求来杀死它。
所以我的问题是。我是否可以以任何方式配置WCF客户端,使其仅接收所有请求并发送最多20个请求,一旦完成,它就会自动分派下一个,依此类推?还是我返回的Task链接到实际的HTTP请求,因此在请求实际分派之前无法返回?
如果是这种情况,而WCF Client根本无法做到这一点,那么我的想法是用一个将命令排队的类来装饰WCF Client,返回一个Task(使用TaskCompletionSource),然后确保不再存在而不是说一次有20个请求处于活动状态。我知道这可以解决问题,但是我想问问是否有人知道做类似这样的事情的库或类吗?
这有点像节流,但我不想这样做,因为我不想限制在给定时间内可以发送多少个请求,而是在任何给定时间内可以存在多少个活动请求时间。
答案 0 :(得分:0)
基于@PanagiotisKanavos建议,这是我如何解决此问题的方法。
RequestLimitCommandService
充当实际服务的装饰器,并以innerSvc
的形式传递给构造器。一旦有人调用ExecuteAsync,就会创建一个完成源,并将其与命令一起发布到ActonBlock
,然后,调用者会从完成源中获取任务。
ActionBlock
随后将调用处理方法。此方法将命令发送到Web服务。根据发生的情况,此方法将使用完成源来通知原始发送方命令已成功处理,或将发生的异常附加到源。
public class RequestLimitCommandService : IAsyncCommandService
{
private class ExecutionToken
{
public TaskCompletionSource<bool> Source { get; }
public ICommand Command { get; }
public ExecutionToken(TaskCompletionSource<bool> source, ICommand command)
{
Source = source;
Command = command;
}
}
private IAsyncCommandService _innerSrc;
private ActionBlock<ExecutionToken> _block;
public RequestLimitCommandService(IAsyncCommandService innerSvc, int maxDegreeOfParallelism)
{
_innerSrc = innerSvc;
var options = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism };
_block = new ActionBlock<ExecutionToken>(Execute, options);
}
public Task IAsyncCommandService.ExecuteAsync(ICommand command)
{
var source = new TaskCompletionSource<bool>();
var token = new ExecutionToken(source, command);
_block.Post(token);
return source.Task;
}
private async Task Execute(ExecutionToken token)
{
try
{
await _innerSrc.ExecuteAsync(token.Command);
token.Source.SetResult(true);
}
catch (Exception ex)
{
token.Source.SetException(ex);
}
}
}