Is it possible to use persistent connections with System.Net.Http.HttpClient? - c#

I have created REST resource using Web API running as a self-hosted process. For performance reasons I would like to be able to call it using persistent HTTP connections. I using OWIN self-hosting.
I really like the asnyc methods for GET, POST, PUT, DELETE in System.Net.Http.HttpClient. They are easy to call and deal with--they return a System.Threading.Tasks.Task which is convenient for what I'm trying to do. I prefer using HttpClient to System.Net.HttpWebRequest.
I'm probably missing something, but it isn't readily apparent to me how to create persistent connections with HttpClient. I'm digging through the System.Net.Http.HttpClientHandler and System.Net.Http.WebRequestHandler classes, but so far I haven't found an option for persistent connections. Google finds all sorts of examples of creating persistent connections using HttpWebRequest. It has a KeepAlive property that can be set to true. Is there a way to set this with HttpClient?
MSDN documentation for HttpClient:
By default, HttpWebRequest will be used to send requests to the server. This behavior can be modified by specifying a different channel in one of the constructor overloads taking a HttpMessageHandler instance as parameter. If features like authentication or caching are required, WebRequestHandler can be used to configure settings and the instance can be passed to the constructor. The returned handler can be passed to one of the constructor overloads taking a HttpMessageHandler parameter.
Is there a way to set the KeepAlive feature on the underlying HttpWebRequest?
The MSDN documentation also says:
The HttpClient class instance acts as a session to send HTTP requests. An HttpClient instance is a collection of settings applied to all requests executed by that instance. In addition, every HttpClient instance uses its own connection pool, isolating its requests from requests executed by other HttpClient instances.
Am I to understand from this that the connection pool will optimize for me using persistent connections when a performance benefit can be gained? What if I want there to only ever be a single connection from my client?

You will need to set the MaxIdle time on the ServicePoint for the client. The easiest way is to set the timeout for all service points:
ServicePointManager.MaxServicePointIdleTime = Timeout.Infinite;
You can also set it on the connection to the specific endpoint
var sp = ServicePointManager.FindServicePoint(targetUri);
sp.MasIdleTime = Timeout.Infinite;

I'm not sure what exactly you are trying to solve but have you looked at SignalR ? They have pretty fancy websocket api's that might do the job you are looking for.
If you are not using .NET 4.5 then they do have alternate mechanisms. One I'v seen being used is something like a server/event-stream.

Related

Why does Blazor template use a factory to create HttpClient instances

When diposing an HttpClient, one can run into the "socket exhaustion" problem. So the standard advice is to reuse the client whenever possible.
So I was surprised to find the official Blazor templates (dotnet new blazorwasm) have this scoped factory in Program.cs:
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
I'm even using the IDisposableAnalyzers analyser library that specifically checks for such things, and it warns to replace that line with a single instance.
Why is the template written that way?
This is what I've found.
It has long been recommended to reuse an HttpClient, due to the socket exhaustion problem.
But Blazor's HttpClient doesn't use TCP for connections, it uses the browser's Fetch API instead. And the Fetch API, according to MDN is JavaScript-based:
The Fetch API provides a JavaScript interface for accessing and manipulating parts of the HTTP pipeline, such as requests and responses. It also provides a global fetch() method that provides an easy, logical way to fetch resources asynchronously across the network.
So the original problem doesn't exist, which means we can safely use as many HttpClient instances as we want.
Which is why the Blazor templates use a factory to create multiple instances.
TL;DR: it should be safe.
My only concern is that the above is based on the Blazor docs, but the framework docs for HttpClient do not mention this special behaviour for Blazor. (I wonder how this works - maybe the Blazor SDK has a different implementation for HttpClient than the server SDK?)
If my analysis is wrong, please let me know.

Am I safe from thread pool starvation/socket issues if I use Flurl in a service thats registered as Transient?

Let's say I have a simple service that's registered as a Transient in Startup, and I use Flurl like so:
public async Task DoStuff()
{
string url = "some valid Url";
await url
.AppendPathSegment("notifications")
.WithHeader("a header", headervalue1)
.WithHeader("another header", headervalue2)
.PostJsonAsync(data);
}
This service will be used a lot throughout our app. Can I count on Flurl to handle the requests efficiently so that my app doesn't exhaust the number of sockets available under heavy loads?
According to their docs: yes - the default usage, as you are showing, makes uses of the implementation guidelines provided by Microsoft:
Quote:
Flurl.Http is built on top of the System.Net.Http stack. If you're familiar with HttpClient, you probably already know this advice:
HttpClient is intended to be instantiated once and re-used throughout the life of an application. Especially in server applications, creating a new HttpClient instance for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors.
Flurl.Http adheres to this guidance by default. Fluent methods like this will create an HttpClient lazily, cache it, and reuse it for every call to the same host*:
Sources:
https://flurl.dev/docs/client-lifetime/
https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-6.0#remarks
Keep in mind though - "heavy load" can still mean you'll hit certain limits like:
reaching maximum server connections - i.e.: possible server overload
reaching maximum client socket usage - i.e.: initiating too many concurrent connections
For more info see:
https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.maxconnectionsperserver?view=net-6.0
This means you'll still need to do a sanity check on the amount of connections you'll be expecting.

.Net Core HttpClientFactory for Multiple API Services

I've got a .Net Core project that needs to connect to around 4 different API services, I'm no expert with any of the HttpClient code, but from what I found, was that you'd generally only want to reuse one instance of your HttpClient. From what I can tell the general consensus is to use the HttpClientFactory in .Net Core by registering it in your Startup class and then requesting it using DI.
Now most of my default headers and such are all generally the same besides the BaseAddress url, how should I go about this when connecting to 4 diff API services? Should I register 4 different named clients or have one client with all the default information pre-set and then manually configure it as needed e.g. configuring the address?
General questions would be as I'm fairly new to this is, it's been said to re-use one instance of an HttpClient.
If I create 4 different named clients for each API service, wouldn't this create 4 instances of the HttpClient when I call the .CreateClient() method?
The .CreateClient() creates a new instance every time it's called, doesn't this defeat the purpose of having one instance of the HttpClient if say I need to make 3 different calls to one API service, each of those calls will call a .CreateClient() to establish some sort of connection and that will create 3 instances of the HttpClient?
Any help for clarity would be appreciated,
Thanks!
The purpose of using IHttpClientFactory is not to reuse instances of HttpClient. Instead, it is to reuse (by pooling) instances of HttpMessageHandler (actually HttpClientHandler, which is derived from the abstract HttpMessageHandler) that is the underlying object that manages HTTP connections & sockets. This diagram from Microsoft Docs shows it well.
You were worried that frequent calls to IHttpClientFactory.CreateClient() will create the same problem as frequent calls to new HttpClient(). However, this is not the case. As explained by Microsoft docs, the reason that frequent calls to new HttpClient() will result in socket exhaustion is that this constructor will create a new instance of HttpMessageHandler:
However, the issue isn't really with HttpClient per se, but with the default constructor for HttpClient, because it creates a new concrete instance of HttpMessageHandler, which is the one that has sockets exhaustion and DNS changes issues mentioned above.
You can see from the source code of IHttpClientFactory that it does not use the parameterless constructor of HttpClient in CreateClient(). Instead, it gets the HttpMessageHandler from a pool and inject it into the created HttpClient.
Whether you are using typed or named clients, you should use the HttpClient instance as if it's a transient object: it is cheap to create and you don't need to cache it for long periods of time.

Does not using the httpClient.BaseAddress on some calls causes a new client creation?

I'm using the .net core 2.1 HttpFactory and I set a BaseAddress in the startup when calling services.AddHttpClient.
Almost all my calls use the BaseAddress, but some need to call other URLs, so for those, I directly pass the full URL.
Does this dispose of my httpClient and create a new one? Should I instead have 2 different client types and register both with their respective BaseAddress in the startup (can I even do that with the HttpFactory?)?
I'm asking the question, as my integration tests have problems with calls using a "timeout style" cancellation token.
PS: Not using the BaseAddress would be a pain, as the URLs depend on the environment.
Update: I tested registering named clients instead. One for the main cases and another one with a different BaseAddress. It seems to have helped my integration tests, as now they rarely timeout (and when they do, I think it may be related to another issue).
This adds to my original question, as it seems to affect the performance. So again, could it be possible that using the same Client for calls using either the BaseAddress or a full address would prompt disposals/recreations of the Client from the HttpFactory?

When should I use OperationContextScope inside of a WCF service?

I'm currently working on a WCF service that reaches out to another service to submit information in a few of its operations. The proxy for the second service is generated through the strongly typed ProxyFactory<T> class. I haven't experienced any issues but have heard I should do something like the following when making the call:
using (new OperationContextScope((IContextChannel)_service))
_service.Send(message);
So my question is: when is creating this new OperationContextScope appropriate, and why?
Thanks!
If you are using callbacks or if you want to modify the message or headers then you need to use OperationContextScope. Your service might need to modify outgoing headers while calling that another service.
When you establish OperationContextScope then you can:
Access and modify incoming and outgoing message headers and other properties.
Access the runtime, including dispatchers, the host, channel, and extensions.
Access other types of contexts, such as security, instance, and request contexts.
Access the channel associated with the OperationContext object or (if the channel implements System.ServiceModel.Channels.ISession) the associated channel's session identifier.
The other service which you call, is it a session-based service? Probably you need to look at its sample client code or documentation if available.

Categories

Resources