HttpClient asyn c запросов не удается - PullRequest
1 голос
/ 01 мая 2020

Мне нужно извлечь контент из примерно 3000 URL. Я использую HttpClient, создаю Task для каждого URL, добавляю задачи в список и затем await Task.WhenAll. Примерно так

    var tasks = new List<Task<string>>();
    foreach (var url in urls) {
        var task = Task.Run(() => httpClient.GetStringAsync(url));
        tasks.Add(task);
    }

    var t = Task.WhenAll(tasks);

Однако многие задачи оказываются в состояниях Faulted или Canceled. Я думал, что это может быть проблема с конкретными URL, но нет. Я могу получить эти URL без проблем с curl параллельно.

Я пробовал HttpClientHandler, WinHttpHandler с различными таймаутами и c. Всегда несколько сотен URL заканчиваются ошибкой. Затем я попытался получить эти URL в партиях по 10, и это работает. Без ошибок, но очень медленно. Curl будет очень быстро получать 3000 URL параллельно. Затем я попытался httpbin.org 3000 раз проверить, что проблема не в моих конкретных URL:

    var handler = new HttpClientHandler() { MaxConnectionsPerServer = 5000 };
    var httpClient = new HttpClient(handler);

    var tasks = new List<Task<HttpResponseMessage>>();
    foreach (var _ in Enumerable.Range(1, 3000)) {
        var task = Task.Run(() => httpClient.GetAsync("http://httpbin.org"));
        tasks.Add(task);
    }

    var t = Task.WhenAll(tasks);
    try { await t.ConfigureAwait(false); } catch { }

    int ok = 0, faulted = 0, cancelled = 0;

    foreach (var task in tasks) {
        switch (task.Status) {
            case TaskStatus.RanToCompletion: ok++; break;
            case TaskStatus.Faulted: faulted++; break;
            case TaskStatus.Canceled: cancelled++; break;

        }
    }

    Console.WriteLine($"RanToCompletion: {ok} Faulted: {faulted} Canceled: {cancelled}");

Опять же, всегда несколько сотен задач заканчиваются ошибкой.

Итак, в чем здесь проблема? Почему я не могу получить эти URL с помощью async?

, который я использую. NET Core и, следовательно, предложение использовать ServicePointManager ( Попытка запустить несколько HTTP-запросов параллельно, но ограничена Windows (реестр) ) не применимо.

Кроме того, URL-адреса, которые мне нужно получить, указывают на разные хосты. Код с httpbin - это всего лишь тест, показывающий, что проблема не в том, что мои URL-адреса неверны.

1 Ответ

1 голос
/ 02 мая 2020

Как сказал Fildor в комментариях, httpClient.GetStringAsync возвращает Task. Так что вам не нужно оборачивать его в Task.Run.

Я запустил этот код в консольном приложении. Это заняло 50 секунд. В своем комментарии вы написали, что curl выполняет 3000 запросов менее чем за минуту - то же самое.

var httpClient = new HttpClient();
var tasks = new List<Task<string>>();
var sw = Stopwatch.StartNew();

for (int i = 0; i < 3000; i++)
{
    var task = httpClient.GetStringAsync("http://httpbin.org");
    tasks.Add(task);
}

Task.WaitAll(tasks.ToArray());
sw.Stop();

Console.WriteLine(sw.Elapsed);
Console.WriteLine(tasks.All(t => t.IsCompleted));

Кроме того, все запросы были успешно завершены.

В вашем коде вы ожидание начала задач с использованием Task.Run. Но вам нужно дождаться завершения запускаемых задач, набрав httpClient.Get...

...