Может ли создание нового клиента WCF для каждого запроса в ASP. NET Ядро привести к исчерпанию сокета? - PullRequest
0 голосов
/ 27 января 2020

В этой статье показана известная проблема с HttpClient, которая может привести к истощению сокетов.

У меня есть ASP. NET Core 3.1 веб-приложение. В библиотеке классов. NET Standard 2.0 я добавил ссылку на веб-службу WCF в Visual Studio 2019, следуя этим инструкциям .

В службе, использующей клиент WCF, как это описано в документации . Создание экземпляра клиента WCF с последующим закрытием клиента для каждого запроса.

public class TestService
{
    public async Task<int> Add(int a, int b)
    {
        CalculatorSoapClient client = new CalculatorSoapClient();
        var resultat = await client.AddAsync(a, b);
        //this is a bad way to close the client I should also check
        //if I need to call Abort()
        await client.CloseAsync();
        return resultat;
    }
}

Я знаю, что закрывать клиент без каких-либо проверок плохо, но для целей этого примера это не имеет значения.

Когда я запускаю приложение и делаю пять запросов к методу действия, использующему клиент WCF, а затем смотрю на результат netstat, я обнаруживаю открытые соединения со статусом TIME_WAIT, очень похожим на проблемы в статье выше о HttpClient.

enter image description here

Мне кажется, что использование встроенного клиента WCF может привести к истощение сокета или я что-то упустил?

Клиент WCF наследуется от ClientBase<TChannel>. Читая эту статью , мне кажется, что клиент WCF использует HttpClient. Если это так, то я, вероятно, не должен создавать новый клиент для каждого запроса, верно?

Я нашел несколько статей ( это и это ) говорить об использовании одиночного или повторного использования клиента WCF. Это способ go?

ОБНОВЛЕНИЕ

Отладка соответствующих частей исходного кода WCF Я обнаружил, что новые HttpClient и HttpClientHandler создавались каждый раз, когда я создавал новый клиент WCF, который Я делаю для каждого запроса. Вы можете проверить код здесь

internal virtual HttpClientHandler GetHttpClientHandler(EndpointAddress to, SecurityTokenContainer clientCertificateToken)
{
    return new HttpClientHandler();
}

Этот обработчик используется для создания нового HttpClient в методе GetHttpClientAsyn c:

httpClient = new HttpClient(handler);

Это объясняет почему клиент WCF в моем случае ведет себя так же, как HttpClient, который создается и удаляется для каждого запроса.

Мэтт Коннов пишет в выпуске в репозитории WCF, что он сделал возможным внедрите свою собственную фабрику HttpMessage в клиент WCF. Он пишет:

Я реализовал возможность предоставить Fun c для включения изменения или замены HttpMessageHandler. Вы предоставляете метод, который принимает HttpClientHandler и возвращает HttpMessageHandler.

Используя эту информацию, я внедрил собственную фабрику, чтобы иметь возможность управлять генерацией HttpClientHandlers в HttpClient.

Я создал моя собственная реализация IEndpointBehavior, которая внедряет IHttpMessageHandlerFactory для получения пула HttpMessageHandler.

public class MyEndpoint : IEndpointBehavior
{
    private readonly IHttpMessageHandlerFactory messageHandlerFactory;

    public MyEndpoint(IHttpMessageHandlerFactory messageHandlerFactory)
    {
        this.messageHandlerFactory = messageHandlerFactory;
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        Func<HttpClientHandler, HttpMessageHandler> myHandlerFactory = (HttpClientHandler clientHandler) =>
        {
            return messageHandlerFactory.CreateHandler();
        };
        bindingParameters.Add(myHandlerFactory);
    }

    <other empty methods needed for implementation of IEndpointBehavior>

}

Как вы можете видеть в AddBindingParameters, я добавляю очень простую фабрику, которая возвращает объединенный HttpMessageHandler.

1064

1064 поведение моего WCF-клиента выглядит следующим образом.

public class TestService
{
    private readonly MyEndpoint endpoint;

    public TestService(MyEndpoint endpoint)
    {
        this.endpoint = endpoint;
    }

    public async Task<int> Add(int a, int b)
    {
        CalculatorSoapClient client = new CalculatorSoapClient();
        client.Endpoint.EndpointBehaviors.Add(endpoint);
        var resultat = await client.AddAsync(a, b);
        //this is a bad way to close the client I should also check
        //if I need to call Abort()
        await client.CloseAsync();
        return resultat;
    }
}

Когда я запускаю приложения с этими изменениями, у меня больше нет открытого соединения для каждого запроса, который я делаю.

Ответы [ 2 ]

0 голосов
/ 24 февраля 2020

Я создал проблему в репозитории WCF в Github и получил сомные ответы клиент или ChannelFactory.

Бониковский пишет:

Создайте одного клиента и используйте его повторно.

var client = new ImportSoapClient();

И Коннью добавляет:

Другая возможность заключается в том, что вы можете создать экземпляр прокси-канала канала из базового канала. Это можно сделать с помощью кода, подобного следующему:

public void Init()
{
    _client?.Close();
    _factory?.Close();
    _client = new ImportSoapClient();
    _factory = client.ChannelFactory;
}

public void DoWork()
{
    var proxy = _factory.CreateChannel();
    proxy.MyOperation();
    ((IClientChannel)proxy).Close();
}

Согласно Connew, нет проблем с повторным использованием клиента в моем ASP. NET Базовом веб-приложении с потенциально параллельными запросами.

Параллельные запросы, использующие один и тот же клиент, не являются проблемой, если вы явно открываете канал, прежде чем будут сделаны какие-либо запросы. Если вы используете канал, созданный из фабрики каналов, вы можете сделать это с помощью ((IClientChannel) proxy) .Open () ;. Я считаю, что сгенерированный клиент также добавляет метод OpenAsyn c, который вы можете использовать.

0 голосов
/ 09 февраля 2020

Вы должны рассмотреть вопрос об утилизации вашего CalculatorSoapClient. Имейте в виду, что простого Dispose() обычно недостаточно, из-за реализации ClientBase. Посмотрите на https://docs.microsoft.com/en-us/dotnet/framework/wcf/samples/use-close-abort-release-wcf-client-resources?redirectedfrom=MSDN, там объясняется проблема.

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

Значение TIME_WAIT также обсуждается здесь:

https://superuser.com/questions/173535/what-are-close-wait-and-time-wait-states

https://serverfault.com/questions/450055/lot-of-fin-wait2-close-wait-last-ack-and-time-wait-in-haproxy

Похоже, ваш клиент сделал все необходимое для закрытия соединения и просто ожидает подтверждения сервер.

Вам не нужно использовать синглтон, так как среда (как правило) хорошо заботится о соединениях.

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