Now our product is being developed on the basis of.Net Core 2.2 platform with use of WCF 4.5 for interaction with SOAP client service. In the process of service operation, our data bus developers had noticed the high load on the server. After that, the problems with access to the service had appeared. The cause was contained in the number of active connections.

There is such a problem as connection exhaustion. It can appear because of lack with accessible ports while making connection or limits to the number of connections with external or internal service. There are two solutions:

  • Increasing the available resources,
  • Decreasing the number of connections.

The first solution is not available for us, because increasing the resources can be done only on the side of the service provider. Thus, we had decided to search for ways to optimize the number of connections. At this article, we are telling about the found solution.



Idea


As it turned out, the issue had been that for every request we created a new copy of WCF client. It had done impossible to use the already realized in WCF connections pool, because this pool is created for each channel, but we, opposite, had created a new channel for every request. Of course, it was possible to rewrite the service responsible for interaction with WCF, for usage of the static WCF client. However, in this case, the pool will be also static and it will cause the issue with change of DNS. There is a solution HttpClientFactory. The core of the solution is in the fact that the factory can work with its own pool, where connections are undated periodically. The update period, in default, is two minutes, but can be changed, if required.

At our product, we had used HttpClientFactory for interaction with other services, and use of the factory in WCF looked as a good alternative to WCF static client. At the same time, we did not need to amend the realization of WCF service. Well, but we could use the pool which the factory can work with. Moreover, it allowed us to resolve the issue with NTLM authorization in Linux, because with the configuration of http client we can set the authentication scheme for the notification handler.

Realization


For working with HttpClientFactory it is enough to add the description of the client configuration to ConfigureServices. There is the possibility to add several named or typed clients with their own configurations. In this case, for each client his own connections pool will be used. For the example below the named client is used.

services.AddHttpClient("ClientName"); 

At WCF, it is possible to add own notification handlers for http clients. For that at binding parameters, we add the delegate by initialized method. There, as an input parameter, we get the handler created on WCF side and return our own handler. As a result, the handler, gotten from the delegate method, will be transferred to the constructor of http client on WCF side.

Thus, returning the handler from the factory pool, we will change the input handler by it. For getting the handler from the factory pool, HttpMessageHandlerFactory is used. In addition, to get access to binding parameters, it is required to realize the class inherited from IEndpointBehavior. Then to add it to our WCF client.

Schematically the algorithm of actions to create a new client on WCF side looks as the following:



We realize CustomEndpointBehaviour.

public class CustomEndpointBehavior : IEndpointBehavior
{
    private readonly Func<HttpMessageHandler> _httpHandler;
    public CustomEndpointBehavior(IHttpMessageHandlerFactory factory)
    {
        //Defining the method of creating the handler for the named client
	_httpHandler = () => factory.CreateHandler("ClientName");
    }
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        //Adding our method to binding parameters
	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) { }
}

After that, we add our EndpointBehavior to WCF client.

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

Now, when creating connections through WCF, copies of handlers from the pool will be used, if possible. It will reduce the number of active connections.

Testing


For checking we had sent 100 similar requests. In the result, without the pool, the peak of connections was 53, and with the pool had not exceeded 7.

Monitoring the connections without the pool:



Monitoring the connections with the pool:



Conclusion


We, in True Engineering, have realized the connections pool at WCF, which does not depend on realization of working with WCF client. Moreover, it effectively saves resources as on the server’s side, where the application launches, as well as on the service provider’s side.

We have spent a lot of time to find ways of optimization, but the solution itself have been gotten out laconic and simple to use. Take and use it, while it is hot)

Комментарии (0)