Мое приложение должно отправлять параллельные HTTP-запросы стороннему REST API. Из-за ограничений со стороны API, мне разрешено делать не более 100 одновременных запросов каждые две секунды. Моя цель - чтобы все партии из 100 запросов выполнялись примерно за одно время (~ 2 секунды). Проблема в том, что в моей текущей реализации первый пакет всегда занимает значительно больше времени, чем остальные.
Приложение представляет собой консольное приложение .NET Core 2.2, работающее в Кубернетесе.
I 'я использую вспомогательную библиотеку, которая позволяет мне легко использовать их REST API. Сигнатура метода, который мне нужно вызвать 100 раз одновременно, выглядит следующим образом:
public static async Task CreateResourceAsync ()
Этот метод создает HTTP-запрос к их API, который создает ресурси их выполнение занимает ~ 2 секунды.
Для выполнения запросов партиями по 100 каждые 2 секунды я написал следующий код:
private ConcurrentQueue<Resource> _resourcesToCreate;
public async Task CreateResources(List<Resource> resources)
{
ThreadPool.GetMinThreads(out _, out var minCompletionPortThreads);
Console.WriteLine("Minimum number of IOCP Threads: {0}",
minCompletionPortThreads);
_resourcesToCreate = resources.ToConcurrentQueue()
while (!_resourcesToCreate.IsEmpty)
{
await ExecuteBatch(100);
await _delayer.Delay(TimeSpan.FromSeconds(2));
}
}
private async Task ExecuteBatch(int batchSize)
{
var requests = new List<Task>();
for (var i = 0; i < batchSize && !_resourcesToCreate.IsEmpty; i++)
{
_resourcesToCreate.TryDequeue(out var resource);
requests.Add(CreateResource(resource));
}
var stopwatch = Stopwatch.StartNew();
await Task.WhenAll(requests);
stopwatch.Stop();
Console.WriteLine($"[Execute Batch] [Complete] | took
{stopwatch.ElapsedMilliseconds}ms");
}
private async Task CreateResource(Resource resource)
{
var resourceCreated = await CreateResourceAsync();
Console.WriteLine("Current IOCP thread ID: {0}",
Thread.CurrentThread.ManagedThreadId);
resource.Id = resourceCreated.Sid;
}
Я запускаю пять тестов, состоящих изделает 500 запросов и вот результаты:
Первая партия:
- Время выполнения: 13088 мс, 15170 мс, 13536 мс, 15398 мс, 11700 мс
- Количество потоков: 5, 6, 5, 19, 3
Вторая партия:
- Время выполнения: 507 мс, 402 мс, 600 мс, 402 мс, 899 мс
- Количество потоков: 3, 6, 4, 26, 2
Третья партия:
- Время выполнения: 306 мс, 98 мс, 501 мс, 99 мс, 1301 мс
- числопотоков: 2, 4, 3, 27, 1
Четвертая партия:
- Времени: 303мс, 98мс, 496мс, 197мс,196мс
- Количество потоков: 2, 6, 3, 7, 2
Пятая партия:
- Время выполнения: 998мс, 108мс, 299мс,194 мс, 97 мс
- Количество потоков: 2, 5, 3, 7, 3
Я знаю, что получение числа различных идентификаторов, напечатанных этим оператором:
Console.WriteLine («Текущий идентификатор потока IOCP: {0}», Thread.CurrentThread.ManagedThreadId);
Скорее всего, это неправильный способ получения количества потоков, использованных во время выполнения пакета. Я попытался использовать метод ThreadPool.GetAvailableThreads (), но он всегда возвращает 1000 потоков IOCP при работе в Kubernetes.
Я хотел бы понять, почему первый пакет всегда занимает больше времени, чем остальные, и может ли это быть возможнобыть связано с количеством используемых потоков?