Хабрахабр

Реализация пула соединений в WCF для .Net Core с использованием HttpClientFactory

Наш продукт разрабатывается на платформе .Net Core 2.2 с использованием WCF 4.5 для взаимодействия с SOAP сервисом клиента. В процессе работы сервиса разработчики шины данных заметили высокую нагрузку на сервер. Далее стали появляться проблемы с доступом к сервису. В результате выяснили, что причина кроется в количестве активных соединений.

Она может возникать из-за нехватки доступных портов при установлении соединения или ограничения на количество соединений с внешним или внутренним сервисом. Существует такая проблема как connection exhaustion. Есть два варианта решения:

• Увеличение доступных ресурсов,
• Уменьшение количества соединений.

Поэтому решили искать варианты оптимизации количества соединений. Первый вариант нам недоступен, так как увеличение ресурсов может производится только на стороне сервис-провайдера. В этой статье расскажем о найденном решении.

Идея

Как выяснилось, проблема заключалась в том, что на каждый запрос мы создавали новый экземпляр WCF клиента. Это делало невозможным использование уже реализованного в WCF пула соединений, потому что пул создаётся для каждого канала, а у нас для каждого запроса создаётся новый канал. Конечно, можно было переписать сервис, отвечающий за взаимодействие с WCF под использование статичного WCF клиента. Но в таком случае пул тоже бы оказался статичным, из-за чего могла возникнуть проблема со сменой DNS, обсуждаемая в этой статье. Там же говорилось о решении – HttpClientFactory. Суть решения заключается в том, что фабрика умеет работать с собственным пулом, соединения в котором периодически обновляются. Период обновления по умолчанию составляет две минуты, но может быть изменен.

При этом нам не пришлось бы вносить изменения в реализацию WCF сервиса. В нашем продукте мы уже использовали HttpClientFactory для взаимодействия с другими сервисами, и использование фабрики в WCF выглядело хорошей альтернативой статичному WCF клиенту. Плюс ко всему это позволяло нам решить проблему с NTLM авторизацией в Linux, описанной в этой статье, так как при конфигурации http клиента можно задать схему аутентификации для обработчика сообщений. Зато смогли бы использовать пул, с которым умеет работать фабрика.

Реализация

Для работы с HttpClientFactory достаточно добавить в ConfigureServices описание конфигурации клиента. Там есть возможность добавить нескольких именованных или типизированных клиентов с собственной конфигурацией. В таком случае для каждого клиента будет использоваться собственный пул соединений. В примере мы используем именованный клиент.

services.AddHttpClient("ClientName");

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

Для получения обработчика из пула фабрики мы используем HttpMessageHandlerFactory. Таким образом, вернув обработчик из пула фабрики, мы заменим им входящий обработчик. И затем добавить его к нашему WCF клиенту. А чтобы получить доступ к параметрам биндинга, необходимо будет реализовать класс, унаследованный от IEndpointBehavior.

Схематически алгоритм действий по созданию нового клиента на стороне WCF выглядит так.

Реализуем CustomEndpointBehaviour.

public class CustomEndpointBehavior : IEndpointBehavior
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { //Добавляем наш метод в параметры биндинга bindingParameters.Add(new Func<HttpClientHandler, HttpMessageHandler>(handler => _httpHandler())); } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { }
}

Далее добавляем наш EndpointBehavior к WCF клиенту.

var httpMessageHandler = serviceProvider.GetRequiredService<IHttpMessageHandlerFactory>();
var client = new WcfClient();
client.Endpoint.EndpointBehaviors.Add(new CustomEndpointBehavior(httpMessageHandler));

Теперь при создании соединений через WCF будут по возможности использоваться экземпляры обработчиков из пула. Это сократит количество активных соединений.

Тест

Для проверки мы отправили 100 одинаковых запросов. В результате без пула пик соединений достигал 53, а с пулом не превысил 7.

Мониторинг соединений без пула:

Мониторинг соединений с пулом:

Заключение

Мы в True Engineering реализовали пул соединений в WCF, который не зависит от реализации работы с WCF клиентом. Также он эффективно экономит ресурсы как на стороне сервера, где запущено приложение, так и на стороне сервис-провайдера.

Берите, пока горячее) Мы потратили много времени на поиск вариантов оптимизации, но само решение получилось лаконичное и простое.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»