When registering with HttpclientFactory, can only do certificate injection in startUp. Is there any way to do dynamic injection?
services.AddHttpClient().ConfigurePrimaryHttpMessageHandler(() => {
var certificate = new X509Certificate2("", "", X509KeyStorageFlags.MachineKeySet);
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(certificate);
return handler;
});
For any future users who stumble across this question, I had a very similar need and was frustrated with this limitation of the default HttpClientFactory implementation; I had a very robust HttpClient pipeline with Polly and delegating handlers all chained up, but the underlying certificate that needed to be used might be different based on the specific endpoint URL, and I didn't necessarily want to register four or five versions of the same pipeline for different typed clients nor did I want to have to have the certificates available at the composition root. I wrote a library to extend DefaultHttpClientFactory to add support for contextually applied handlers while retaining the pooling, expiry, and typed client pipelines that DefaultHttpClientFactory provides.
https://github.com/agertenbach/Ringleader
https://www.nuget.org/packages/Ringleader
The DefaultHttpClientFactory uses the named client's name or typed client's type name to uniquely distinguish and track both the pipeline and options you attach at startup, as well as the name that the resultant primary handler will have in the managed pool; for example, a typed client "MyTypedClient" will have primary handlers in the pool named "MyTypedClient" that will get reused and renewed as needed.
By decorating the HttpClientFactoryOptions IOptionsMonitor that DefaultHttpClientFactory uses and hooking in an IHttpMessageHandlerBuilderFilter during the handler management processes, it's possible to keep all the pipeline stuff working, but create and resolve more granular handlers in the pool, i.e. "MyTypedClient-contextA", "MyTypedClient-contextB", that have different contextual settings, such as a different certificate. The library just requires you implement a couple interfaces to distinguish those contexts and then return a well-formed primary handler for that context when a new one must be created.
This still is not ideal if your certificates are going to be different on every request or very infrequently reused (as you're losing all the benefits of the pooling anyways), but if you have a well-developed typed client pipeline that has a different cert for URL A vs URL B (or user A vs user B, etc.) and you're connecting to them all fairly frequently, this might save you some headaches. Happy for any feedback or comments if this helps you out.
The delegate you are passing in for ConfigurePrimaryHttpMessageHandler is called depending on its lifetime (two minutes by default). So in that delegate you can actually make a dynamic list of certificates (and it will called every two minutes -when you create the HttpClient instance-).
You can choose the lifetime of the handler using SetHandlerLifeTime (on the builder on AddHttpClient()) and choose a shorter time span if it needs be... however if you need a different handler for the same client many times, that defies the whole purpose of using the HttpClientFactory.
So I see three options:
If you have sets of different certificates, use named or typed clients and assign a different handler for each name/type.
If, instead, your http client "generally" uses the same certificates and they just change at some point in time... then set a reasonable lifespan (with SetHandlerLifetime) depending on your application needs.
If you actually need the certificates to be defined per-request (or per-instance)... then don't use the HttpClientFactory or any other client pooling method because you do not want to have the clients pooled at all and you want a different one (with its own handler) per request.
Related
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.
Currently we are tied to a single Azure Service Bus instance for local and the test environment (I would prefer a different setup but It's not up to me). Previously we were using a custom framework that for each topic it created a subscrption with a name that was a mix of the service name and, if the project was run by a dev in his machine, it appended a "local name" to avoid having all the dev local computers and the server instances competing for the same message. So for example for the ShoppingService it created a subscription called ShoppingService for the server instances and in Joe's local machine it created a subscription called let's say ShoppingService-JoeComputer. Right now we are trying to switch from this custom framework to MassTransit because we like the additional features it provides (and also because honestly the custom framework was more buggy than we would like to admit). We followed this video published by Patterson to create the commands, events and consumers and everything works as described:
MassTransit - Using Azure Service Bus
However, we are now facing the competition issue I mentioned before. To solve it we will try to set the custom naming convention we are currently using so the questions I have are:
Is there a way to modify the default naming convention used by MassTransit? We would like to keep most of it where it creates a topic based on the command type name but we would like to add a postfix to both the forwarding subscription and the queue. In other words if we were talking about the submit order command described in the video we would like the subscription name to be "submit-order-joe" and the queue name to be "submit-order-joe" if it runs on joe's machine. We could set up the configuration manually for each consumer/client/publisher but it would be great if we can set it up "globally".
Using the default convention, what happens if there are two commands named "namespaceOne.SubmitOrder" and "namespaceTwo.SubmitOrder"? Based on what we saw it would create two different topics but both subscriptions would be called "submit-order" and both would forward messages to the same queue called "submit-order". That would be confusing and I don't even know if MassTransit is going to consume the commands correctly.
There are two naming conventions in MassTransit. Entity names (which are based upon message type, and would be topics in Azure Service Bus) and endpoint names (which are based upon the consumer, saga, or activity type and would be queues in ASB).
You can specify your own entity name formatter to customize the entity name format, or you can override specific messages.
You can also specify your own endpoint name formatter, described in this video to customize the queue names generated for receive endpoints. Or you can create an instance of the built-in formatters specifying different constructor arguments to include a prefix, include the namespace, etc.
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.
We are creating range of dotnet core 2.0 microservices based on the servicestack framework. We want to use http-header based correlation tokens, so we can track a request in our distributed logging system (Seq).
We would like to use IoC to setup a a class holding a threadsafe JsonServiceClient for performance reasons, but how can we ensure that headers placed on one thread will not leak into another concurrent request? Client code example:
public TResponse Get(IReturn requestDto)
...
_serviceClient.AddHeader("r-id", theReqId); // how can we make these specific for the thread request only?
var responseFromDownstreamService = _serviceClient.Get(requestDto);
If you’re modifying the service client instance the dependency needs to be transient so each thread receives a new instance they can mutate without modifying the same instance used by other threads.
I have a translation service and I need to expose a property that I want to use across my application.
services.AddScoped<IMyTranslator, MyTranslator>();
I use services.AddScoped to register this interface and its implementation. When I set a breakpoint to the constructor of MyTranslator, I can see that this class is initialized all the time.
I could not use AddSingleton, because it's shared across all sessions. I can not change the language for one user base on another user has changed language once.
I need a method that will initialize my middleware once per session.
The services.AddScoped is already scoped to the user request, each time a user request reaches the server, an instance is created to serve this specific request only and it is not shard with other users.