WCF Connection Pool Implementation for .Net Core Using HttpClientFactory

Our product is developed on the .Net Core 2.2 platform using WCF 4.5 to interact with the SOAP client service. During the service, the data bus developers noticed a high load on the server. Further, problems with access to the service began to appear. As a result, it was found that the reason lies in the number of active compounds.



There is such a problem as connection exhaustion. It may arise due to the lack of available ports when establishing a connection or limiting the number of connections to external or internal services. There are two possible solutions:



• Increasing available resources,

• Reducing the number of connections.



The first option is not available to us, since an increase in resources can be made only on the side of the service provider. Therefore, we decided to look for options for optimizing the number of compounds. In this article we will talk about the solution found.







Idea



As it turned out, the problem was that for each request we created a new instance of the WCF client. This made it impossible to use the connection pool already implemented in WCF, because a pool is created for each channel, and we create a new channel for each request. Of course, it was possible to rewrite the service responsible for interacting with WCF using a static WCF client. But in this case, the pool would also be static, which could cause a problem with the DNS change, discussed in this article . It also talked about the solution - HttpClientFactory . The essence of the solution is that the factory can work with its own pool, in which connections are periodically updated. The default update period is two minutes, but can be changed.



In our product, we have already used HttpClientFactory to interact with other services, and using the factory in WCF looked like a good alternative to a static WCF client. In this case, we would not have to make changes to the implementation of the WCF service. But they could use the pool that the factory can work with. In addition, it allowed us to solve the problem with NTLM authentication in Linux, described in this article , since when configuring the http client, you can set the authentication scheme for the message handler.



Implementation



To work with HttpClientFactory, just add the client configuration description to ConfigureServices. There you can add several named or typed clients with your own configuration. In this case, each client will use its own connection pool. In the example, we use a named client.



services.AddHttpClient("ClientName");
      
      





In WCF, you can add your own message handlers for the http client. To do this, add a delegate initialized by the method to the binding parameters. There, as an input parameter, we get a handler created on the WCF side and return our own handler. As a result, the handler obtained from the delegate method will be passed to the client http designer on the WCF side.



Thus, returning the handler from the factory pool, we will replace the incoming handler with it. To get the handler from the factory pool, we use the HttpMessageHandlerFactory. And to get access to the binding parameters, it will be necessary to implement a class inherited from IEndpointBehavior. And then add it to our WCF client.



Schematically, the algorithm for creating a new client on the WCF side looks like this.







We implement CustomEndpointBehaviour.



 public class CustomEndpointBehavior : IEndpointBehavior { private readonly Func<HttpMessageHandler> _httpHandler; public CustomEndpointBehavior(IHttpMessageHandlerFactory factory) { //       _httpHandler = () => factory.CreateHandler("ClientName"); } 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) { } }
      
      





Next, add our EndpointBehavior to the WCF client.



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





Now when creating connections through WCF, whenever possible, handler instances from the pool will be used. This will reduce the number of active compounds.



Test



For verification, we sent 100 identical requests. As a result, without a pool, the peak of compounds reached 53, but with a pool it did not exceed 7.



Monitoring connections without a pool:







Monitoring pool connections:







Conclusion



We at True Engineering implemented a connection pool in WCF, which does not depend on the implementation of working with the WCF client. It also effectively saves resources both on the server side where the application is running, and on the side of the service provider.



We spent a lot of time searching for optimization options, but the solution itself was concise and simple. Take it while it’s hot)



All Articles