Сбой HttpClient с ResponseHeadersRead (таймауты) при второй попытке GetAsync без Fiddler (отладчик Http / Https) - PullRequest
1 голос
/ 15 июня 2019

Я пытаюсь получить код состояния какой-либо страницы.

Проблема по умолчанию - метод GetAsync возвращает всю страницу с содержимым, в то время как мне нужен только заголовок для проверки состояния страницы (404,403 и т. Д.),что в конечном итоге приведет к перегружению памяти, поскольку мне придется проверять тонны URI.

Я добавил опцию ResponseHeadersRead, чтобы решить эту проблему с запоминанием памяти, но затем этот код начал выдавать исключение «Задача была отменена»,что означает тайм-аут.

Вещи, которые я знаю:

  1. Код ResponseHeadersRead работает ТОЛЬКО, когда я запускаю fiddler (Http / Https Debugger) на моем локальном ПК.

  2. Код ResponseHeadersRead работает в среде онлайн-кодирования, такой как dotnetfiddle.но не работает в среде ОС Windows.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Security.Cryptography;


public class Program
{
    public static string[] Tags = { "first", "second" };
    public static string prefix = null;
    static HttpClient Client = new HttpClient();
    public static void Main()
    {
        System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
        Client.DefaultRequestHeaders.ConnectionClose = true;

        // limit parallel thread
        Parallel.ForEach(Tags,
        new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 1.0)) },
        tag =>
        {
            for (int i = 1; i < 4; i++)
            {
                switch (i)
                {
                    case 1:
                        prefix = "1";
                        break;
                    case 2:
                        prefix = "2";
                        break;
                    case 3:
                        prefix = "3";
                        break;
                }
                Console.WriteLine(tag.ToString() + " and " + i);
                HttpResponseMessage response = Client.GetAsync("https://example.com/" + prefix).Result; // this works
//                HttpResponseMessage response = Client.GetAsync("https://example.com/" + prefix,HttpCompletionOption.ResponseHeadersRead).Result; // this fails from 2nd try with one url.
                Console.WriteLine(i + " and " + (int)response.StatusCode);
                if (response.StatusCode != HttpStatusCode.NotFound)
                {

                }

            }
        });

    }
}

Он получает время ожидания потока с помощью ResponseHeadersRead, но не без него.

enter image description here

Ответы [ 2 ]

2 голосов
/ 15 июня 2019

Не используйте Parallel для async кода, он предназначен для привязки к процессору. Вы можете выполнять все запросы одновременно, не тратя на него потоки блокирующих. Способ решить эту проблему - не увеличивать DefaultConnectionLimit, однако это решит это в этом случае. Правильный способ справиться с ResponseHeadersRead - это либо Dispose response, т.е.

using(HttpResponseMessage response = Client.GetAsync("https://example.com/" + prefix, HttpCompletionOption.ResponseHeadersRead).Result) {}

или прочитать Content ответа.

var data = response.ReadAsStringAsync().Result;

С ResponseHeadersRead вам нужно сделать это для того, чтобы соединение было закрыто. Я бы посоветовал вам переписать этот код, чтобы избавиться от Parallel, а не звонить .Result на ваши async звонки.

Вы можете сделать что-то вроде этого:

private static async Task Go()
{
    System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
    Client.DefaultRequestHeaders.ConnectionClose = true;

    var tasks = Tags.Select(tag =>
    {
        var requests = new List<Task>();
        for (int i = 1; i < 4; i++)
        {
            switch (i)
            {
                case 1:
                    prefix = "1";
                    break;
                case 2:
                    prefix = "2";
                    break;
                case 3:
                    prefix = "3";
                    break;
            }

            requests.Add(MakeRequest(Client, prefix, tag));
        }
        return requests;
    }).SelectMany(t => t);

    await Task.WhenAll(tasks);
}

private async static Task MakeRequest(HttpClient client, string prefix, string tag)
{

    using (var response = await client.GetAsync("https://example.com/" + prefix, HttpCompletionOption.ResponseHeadersRead))
    {
        Console.WriteLine(tag + " and " + prefix);
        Console.WriteLine(prefix + " and " + (int)response.StatusCode);
    }
}
2 голосов
/ 15 июня 2019

Когда вы устанавливаете ResponseHeadersRead, вы указываете клиенту HTTP читать только заголовки HTTP из каждого ответа, поэтому соединение TCP / IP, по которому выполняется запрос, находится в середине ответа, пока вы не прочитаете тело ответа. .

И есть ограничение на то, сколько соединений HttpClient откроет с любым конкретным веб-сайтом. По умолчанию используется значение 2. Таким образом, вы открываете два соединения и пытаетесь открыть третье, которое блокирует ожидание доступного соединения.

Вы можете просто увеличить лимит подключения для вашего приложения.

например:

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