Регулирование HttpClient.NET
Я разрабатываю приложение на основе.NET4, которое должно запрашивать сторонние серверы для получения информации от них. Я использую HttpClient для выполнения этих HTTP-запросов.
Я должен создать сотню или тысячу запросов за короткий промежуток времени. Я хотел бы ограничить создание этих запросов до предела (определенного константой или чем-то еще), чтобы другие серверы не получали много запросов.
Я проверил эту ссылку, которая показывает, как уменьшить количество задач, созданных в любое время.
Вот мой нерабочий подход:
// create the factory
var factory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(level));
// use the factory to create a new task that will create the request to the third-party server
var task = factory.StartNew(() => {
return new HttpClient().GetAsync(url);
}).Unwrap();
Конечно, проблема здесь в том, что даже при создании одной задачи одновременно будет создаваться и обрабатываться множество запросов, поскольку они выполняются в другом планировщике. Я не смог найти способ изменить планировщик на HttpClient.
Как мне справиться с этой ситуацией? Я хотел бы ограничить количество запросов, созданных до определенного предела, но не блокировать ожидание завершения этих запросов.
Это возможно? Есть идеи?
4 ответа
Вы могли бы рассмотреть создание нового DelegatingHandler для размещения в конвейере запросов / ответов HTTPClient, который мог бы вести подсчет количества ожидающих запросов.
Обычно один экземпляр HTTPClient используется для обработки нескольких запросов. В отличие от HttpWebRequest, удаление экземпляра HttpClient закрывает базовое соединение TCP/IP, поэтому, если вы хотите повторно использовать соединения, вам действительно нужно повторно использовать экземпляры HTTPClient.
Если вы можете использовать.Net 4.5, один из способов будет использовать TransformBlock
из потока данных TPL и установите его MaxDegreeOfParallelism
, Что-то вроде:
var block = new TransformBlock<string, byte[]>(
url => new HttpClient().GetByteArrayAsync(url),
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = level });
foreach (var url in urls)
block.Post(url);
block.Complete();
var result = new List<byte[]>();
while (await block.OutputAvailableAsync())
result.Add(block.Receive());
Существует также другой способ взглянуть на это через ServicePointManager
, Используя этот класс, вы можете установить ограничения на MaxServicePoints
(к скольким серверам вы можете подключиться одновременно) и DefaultConnectionLimit
(сколько подключений может быть к каждому серверу). Таким образом, вы можете начать все свои Task
в тот же момент, но только ограниченное количество из них действительно что-то сделают. Хотя ограничение количества Task
s (например, с использованием потока данных TPL, как я предложил выше), скорее всего, будет более эффективным.
Вы можете рассмотреть возможность запуска фиксированного набора потоков. Каждый поток выполняет клиентские сетевые операции последовательно; возможно также приостановка в определенных точках, чтобы задушить. Это даст вам определенный контроль над загрузкой; Вы можете изменить свою политику газа и изменить количество потоков.
Во-первых, вы должны рассмотреть возможность разделения рабочей нагрузки в соответствии с веб-сайтом или, по крайней мере, предоставить абстракцию, позволяющую выбрать способ разделения списка URL-адресов. Например, одна стратегия может относиться к домену второго уровня, например, yahoo.com, google.com.
Другое дело, что если вы делаете серьезное сканирование, вы можете вместо этого рассмотреть возможность сделать это в облаке. Таким образом, каждый узел в облаке может сканировать другой раздел. Когда вы говорите "короткий промежуток времени", вы уже настраиваете себя на неудачу. Вам нужны точные цифры о том, что вы хотите достичь.
Другим ключевым преимуществом хорошего разделения является то, что вы также можете избежать попадания на серверы в часы их пиковой нагрузки и риска запрета IP-адресов на уровне маршрутизатора в случае, если сайт не просто душит вас.