public class RollingRequests
{
private const int DefaultNumSimultaneousRequests = 10;
private readonly HttpClient _client; // Don't worry about disposing see https://stackoverflow.com/questions/15705092/do-httpclient-and-httpclienthandler-have-to-be-disposed
private readonly HttpCompletionOption _httpCompletionOption;
private readonly int _numSimultaneousRequests;
public RollingRequests() : this(DefaultNumSimultaneousRequests)
{
}
public RollingRequests(int windowSize) : this(new HttpClient(), windowSize)
{
}
public RollingRequests(HttpClient client, int numSimultaneousRequests, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead)
{
_client = client;
_numSimultaneousRequests = numSimultaneousRequests;
_httpCompletionOption = httpCompletionOption;
}
public async Task ExecuteAsync(List<string> urls, CancellationToken cancellationToken, Action<HttpResponseHeaders, string> requestCallback = null)
{
var nextIndex = 0;
var activeTasks = new List<Task<Tuple<string, HttpResponseMessage>>>();
var startingIndex = Math.Min(_numSimultaneousRequests, urls.Count);
for (nextIndex = 0; nextIndex < startingIndex; nextIndex++)
{
activeTasks.Add(RequestUrlAsync(urls[nextIndex], cancellationToken));
}
while (activeTasks.Count > 0)
{
var finishedTask = await Task.WhenAny(activeTasks).ConfigureAwait(false);
activeTasks.Remove(finishedTask);
var retryUrl = await ProcessTask(await finishedTask, requestCallback).ConfigureAwait(false);
// If retrying, add the URL to the end of the queue
if (retryUrl != null)
{
urls.Add(retryUrl);
}
if (nextIndex < urls.Count)
{
activeTasks.Add(RequestUrlAsync(urls[nextIndex], cancellationToken));
nextIndex++;
}
}
}
private async Task<string> ProcessTask(Tuple<string, HttpResponseMessage> result, Action<HttpResponseHeaders, string> requestCallback = null)
{
var url = result.Item1;
using (var response = result.Item2)
{
if (!response.IsSuccessStatusCode)
{
return url;
}
if (requestCallback != null)
{
string content = null;
if (_httpCompletionOption == HttpCompletionOption.ResponseContentRead)
{
content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
requestCallback(response.Headers, content);
}
return null;
}
}
private async Task<Tuple<string, HttpResponseMessage>> RequestUrlAsync(string url, CancellationToken ct)
{
var response = await _client.GetAsync(url, _httpCompletionOption, ct).ConfigureAwait(false);
return new Tuple<string, HttpResponseMessage>(url, response);
}
}
Это класс, который позволяет одновременно выполнять X одновременных запросов.Когда я тестирую этот класс модулем, и я запускаю HttpClient, который дает каждому запросу 1-секундную задержку, первоначальный activeTasks.Add
занимает 5 секунд, если у меня есть 5 запросов, предлагая мне, что RequestUrlAsync не является действительно асинхронным.
Может кто-нибудь заметить проблему?
Редактировать: Вот как я сплю осмеянный клиент
_messageHandlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(MethodToMoq, ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Callback(() => Thread.Sleep(1000))
.ReturnsAsync(callback)
.Verifiable();