Выполняя 100 одновременных HTTP-запросов каждые две секунды, первый пакет занимает значительно больше времени, чем остальные - PullRequest
1 голос
/ 03 октября 2019

Мое приложение должно отправлять параллельные 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.

Я хотел бы понять, почему первый пакет всегда занимает больше времени, чем остальные, и может ли это быть возможнобыть связано с количеством используемых потоков?

...