Регулирование 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-адресов на уровне маршрутизатора в случае, если сайт не просто душит вас.

Другие вопросы по тегам