Можно ли запустить 3000 параллельных асинхронных HTTP-запросов из основного контроллера ASP.NET? - PullRequest
0 голосов
/ 19 марта 2019

У меня есть контроллер веб-API, который получает объект фильтра от внешнего интерфейса.

Фильтр - это просто массив объектов json, который содержит критерии фильтрации.

Теперь, основываясь на этом фильтре, я должен выполнить запрос к API Azure (API аналитики журнала Azure).

Это показано в следующем фрагменте кода:

 var tasks = filters.Select(filter=>
            {
                try
                {
                    return service.GetPerformanceCounters(filter.SComputerId, filter.WorkSpace);
                }
                catch (Exception ex)
                {
                    return Task.Run(() => new List<Metrics>());
                }
            })
            .ToList();

            var metrics = (await Task.WhenAll(tasks))
                                    .SelectMany(metric => metric)
                                    .ToList();

Это в основном означает, что, если у меня 3000 объектов фильтра, мне придется выполнить 3000 асинхронных параллельных запросов.

Но когда я тестировал этот код, когда добирался до 100 фильтрующих объектов, асинхронные параллельные задачи не выполнялись.

Я получаю следующее исключение на панели отладки:

Исключение: «System.Net.Http.HttpRequestException» в Azure.DAL.dll. Исключение: «System.Net.Http.HttpRequestException» в System.Private.CoreLib.dll. Исключение: «System.Net.Http.HttpRequestException».в System.Private.CoreLib.dll сгенерировано исключение: «System.Net.Http.HttpRequestException» в Azure.DAL.dll сгенерировано исключение: «System.Net.Http.HttpRequestException» в System.Private.CoreLib.dll сгенерировано исключение:Исключение «System.Net.Http.HttpRequestException» в System.Private.CoreLib.dll: «Исключение System.Net.Http.HttpRequestException» в System.Private.CoreLib.dll: «System.Net.Http.HttpRequestException» вSystem.Private.CoreLib.dll. Исключение: «System.Net.Http.HttpRequestException» в Azure.DAL.dll. Исключение: «System.Net.Http.HttpRequestException» в.System.Private.CoreLib.dll Сгенерировано исключение: «System.Net.Http.HttpRequestException» в Azure.DAL.dll Сгенерировано исключение: «System.Net.Http.HttpRequestException» в Azure.DAL.dll Сгенерировано исключение: «System.NetИсключение .Http.HttpRequestException в System.Private.CoreLib.dll. Исключение: «System.Net.Http.HttpRequestException» в System.Private.CoreLib.dll. Исключение: «System.Net.Http.HttpRequestException» в System.Private.Выдано исключение CoreLib.dll: «System.Net.Http.HttpRequestException» в System.Private.CoreLib.dll Поток 0x3eac завершился с кодом 0 (0x0).Нить 0x2294 вышла с кодом 0 (0x0).Нить 0x66b0 вышла с кодом 0 (0x0).Нить 0x6958 вышла с кодом 0 (0x0).Поток 0xb5c вышел с кодом 0 (0x0).Нить 0x6a98 вышла с кодом 0 (0x0).Нить 0x16e8 вышла с кодом 0 (0x0).Поток 0x28f8 завершился с кодом 0 (0x0).

Как сделать возможным выполнение более 100 асинхронных HTTP-запросов без этой проблемы, я не могу контролировать количество фильтров, отправленных изспереди, но мне нужно иметь возможность выполнять несколько параллельных асинхронных операций, эквивалентных количеству фильтров.

Редактировать: Официальный запрошенный документ конечной точки включен.

https://dev.loganalytics.io/documentation/Using-the-API/Limits

В документе указано, что:

При использовании аутентификации по ключу API для выборки данных применяются правила регулирования для каждого IP-адреса клиента.Каждый IP-адрес клиента может выполнять до 200 запросов в течение 30 секунд без ограничения общего количества вызовов в день.

Редактировать: реализация GetPerformanceCounters.

        public async Task<List<Entities.Metrics>> GetPerformanceCounters(string sourceComputerId, string workspaceId)
    {

        Debug.WriteLine("New Task lauched");


        // The log analytics query 
          var query = $@"Perf 
        | where SourceComputerId == '{sourceComputerId}'
        | summarize arg_max(TimeGenerated, *) by SourceComputerId";




        var response = await RemoteKustoProvider.RunWorkSpaceQueryAsync
                  (
                        workspace: workspaceId,
                        query: query
                 );

        Debug.WriteLine("Task Finished");

        // Send query to the Kusto-Engine. 
        return JsonConvert.DeserializeObject<List<Entities.Metrics>>
        (
           response
       );
    }

Заранее благодарим за помощь в решении этой проблемы.

1 Ответ

0 голосов
/ 19 марта 2019

Здесь число вопросов. Прежде всего, вы должны понимать, что вся активная работа требует потока. Эти потоки приходят из пула потоков, который обычно устанавливается равным 1000 из коробки. Это означает, что с самого начала вы исчерпываете пул потоков с таким количеством задач, так что, скорее всего, 2000 из них находятся в очереди. Async помогает , но это не волшебство. Некоторые потоки могут в конечном итоге вернуть пул после того, как запрос завершится, но переключение потоков не гарантируется. Несмотря на это, вы по-прежнему истощаете пул потоков быстрее, чем можете его заполнить.

Тогда неясно, что на самом деле происходит здесь, потому что есть несколько слоев, и вы кормите нас ложкой за один раз. Вы опубликовали, что делает GetPerformanceCounters, но теперь неясно, что делает RemoteKustoProvider.RunWorkSpaceQueryAsync. В какой-то момент вы используете что-то вроде HttpClient, чтобы в конечном итоге сделать запрос в Azure. В зависимости от того, как вы это делаете (и, честно говоря, исходя из качества этого кода, я не очень надеюсь, что вы делаете это правильно), вы, вероятно, также исчерпывает пул соединений. Это гораздо более ограниченный ресурс, так что это означает, что ваша пропускная способность резко снижается, что приводит к дальнейшему увеличению объема цепочки.

Далее, даже если вы могли бы просто взорвать все 3000 запросов одновременно, это по сути равносильно DDoS-атаке на сервер получателя. Я совсем не удивлюсь, если вы на самом деле задушены или заблокированы брандмауэрами на другом конце. Это, конечно, - как вы уже догадались, - является узким местом и во всем остальном.

Наконец, даже если все эти функции, как вы и предполагали, запуск 3000 задач - это не то же самое, что «параллельная обработка». Для этого вам нужно использовать класс Parallel из TPL.

Я не уверен, что ваша конечная цель здесь, но, безусловно, есть более эффективный способ сделать это, отправив 3000 отдельных запросов. Если там действительно нет, то вам нужно их пакетировать - отправлять несколько раз в течение более длительного периода. Весь процесс должен быть перенесен на внешний процесс и должен быть просто запланирован вашим веб-приложением. Затем вы можете использовать что-то наподобие SignalR для отправки обновлений и, в конечном итоге, обратно к пользователю, когда все это завершится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...