HttpClient зависает на GetAsync при выполнении многих веб-запросов к localhost - PullRequest
0 голосов
/ 12 октября 2018

У меня проблема ниже при звонке api/Values/Test?times={x}.Эта проблема не возникает, когда x равен 1, 2, ..., но веб-запрос истекает, когда он достигает 9, 10 и т. Д.Как видите, конечная точка Test будет отправлять веб-запрос EndPoint x раз (извините за бессмысленные тела методов, они на практике будут делать более значимые вещи):

public class ValuesController:  System.Web.Http.ApiController
{
    [HttpGet]
    public async Task<object> Test(int times)
    {
        var tasks = new List<Task<int>>();
        for (var i = 0; i < times; i++)
        {
            //Make web request to the `EndPoint` method below
            var task = new ApiRequestBuilder<int>("https://localhost:44301/api/Values/EndPoint")
                .GetAsync();
            tasks.Add(task);
        }
        return await Task.WhenAll(tasks.ToArray());
    } 

    [HttpGet]
    [CustomAuthorise]//This makes a web request to `AuthEndPoint` (below)
    public int EndPoint()
    {
        return 0;
    }

    [HttpGet]
    public bool AuthEndPoint()
    {
        return true;
    }
}

( NB см. Ниже, что такое ApiRequestBuilder)

Проблема, однако, заключается не в том, что EndPoint ударяют x раз, а в атрибуте CustomAuthorise, который делаетследующий веб-запрос - вот проблема:

public class CustomAuthoriseAttribute : System.Web.Http.AuthorizeAttribute
{
    public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        var result = await this.AuthoriseAsync(actionContext);
        if (!result)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
        }
    }

    public virtual async Task<bool> AuthoriseAsync(HttpActionContext context)
    {
        //***********HANGING HERE************
        //var result= await new ApiRequestBuilder<bool>("https://www.example.com")
        var result= await new ApiRequestBuilder<bool>("https://localhost:44301/Values/AuthEndPoint")
            .GetAsync();

        return result;
    }
}

Как видите, я закомментировал маршрут "https://www.example.com".Когда я задаю URL-адрес, который не является localhost, веб-запрос может спамить (путем установки x на большое число), сколько бы я ни хотел.

ApiRequestBuilder - это помощник для создания веб-запросов через System.Net.Http.HttpClient (версия 4.0.0.0, .NETframework v4.5.1), который сериализует ответ на заданный вами параметр универсального типа.

public class ApiRequestBuilder<T> 
{
    protected string Url => Uri?.AbsoluteUri;
    protected Uri Uri;
    protected readonly HttpClient Client;

    public ApiRequestBuilder(string url) 
    {
        Uri = new Uri(url);
        var client = new HttpClient()
        {
            BaseAddress = this.Uri,
            //Set timeout so replicating this issue itn't a pain
            Timeout = TimeSpan.FromSeconds(5)
        };
        this.Client = client;       
    }

    public async Task<T> GetAsync()
    {
        var response = await this.Client.GetAsync(this.Url);
        var responseContent = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(responseContent);
    }
}

Вещи, которые я пробовал:

  • Имеют оператор using для каждого использования HttpClient в ApiRequestBuilder - все еще зависает.

  • Ожидайте каждый Task в цикле for в ValuesController.Test - это работает, но не решает мою проблему, так как на практике я пытаюсь симулировать параллелизм для использования моего пользовательского атрибута auth.

Потенциально полезно:

  • Я использую локальный IIS на порту 44301, максимальное количество одновременных подключений велико - 4294967295.

  • Как я уже сказал, изменение URL-адреса с localhost на что-то вроде example.com при авторизации устраняет проблему, которая изолирует проблему от того, что что-то не так с моей локальной настройкой

Кто-нибудь знает, почему использование веб-запроса в атрибутах авторизации кажется зависшим при одновременном использовании?g localhost вместо того, чтобы использовать URL в большом мире?

ОБНОВЛЕНИЕ

Запуск netstat -n в точке зависания (я удалил тайм-аутдля этого, чтобы я мог посмотреть, что делают соединения TCP).Для каждого выполняемого веб-запроса, который, казалось бы, был заблокирован, у меня было следующее:

Proto  Local Address          Foreign Address        State
TCP    [::1]:44301            [::1]:44728            CLOSE_WAIT
TCP    [::1]:44728            [::1]:44301            FIN_WAIT_2

С помощью this создается впечатление, что сервер попросил клиента выйти, клиентыпризнал это, но они оба зависают (клиент не закрывается?).

Ответы [ 2 ]

0 голосов
/ 13 октября 2018

Это решение для.Net core 2.1, но это может объяснить вашу проблему: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests

0 голосов
/ 12 октября 2018

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

Ссылка ВЫ ИСПОЛЬЗУЕТЕ НЕПРАВИЛЬНО HTTPCLIENT И УДАЛЯЕТЕ ВАШЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ

Использование одного статического клиента потенциально может решить эту проблему

public class ApiRequestBuilder<T>  {
    protected string Url => Uri?.AbsoluteUri;
    protected Uri Uri;
    static HttpClient Client = new HttpClient() {
        //Set timeout so replicating this issue itn't a pain
        Timeout = TimeSpan.FromSeconds(5)
    };

    public ApiRequestBuilder(string url) {
        Uri = new Uri(url);        
    }

    public async Task<T> GetAsync() {
        var response = await Client.GetAsync(this.Url);
        var result = await response.Content.ReadAsAsync<T>();
        return result;
    }
}
...