Dirk Richter
Software-Entwicklung und Architektur
Nutzung des HttpClient mit Factory
Der HttpClient verwendet den HttpMessageHandler, um Request über den SocketsHttpHandler über das Netzwerk zu verschicken. D.h. der HttpClient hat Abhängigkeiten zum SocketsHttpHandler, die in der Folge hier analysiert werden. Die Klasse HttpClient implementiert IDisposable. Daher liegt es nahe, das Objekt über using zu verwenden. Das Problem hierbei ist jedoch, dass der HttpClient intern SocketsHttpHandler verwendet, der die Connections verwaltet. Wenn der HttpClient disposed wird, wird auch SocketsHttpHandler disposed, was zu unerwünschten Nebeneffekten führen kann, wenn die Verbindung plötzlich geschlossen wird. Bei jeder Erzeugung eine HttpClients muss auch der SocketsHttpHandler neu erzeugt werden und die Verbindung neu erstellt werden. Die geöffneten Sockets kann man über den Befehl anzeigen lassen. Um eine Socket Exhaustion zu vermeiden, liegt es nahe, eine langlebige Instanz des HttpClients zu erzeugen. In der Folge wird der SocketsHttpHandler nicht disposed und der Socket wird wieder verwendet. Es gibt jedoch Szenarien, in denen dies zu unerwünschten Nebeneffekten führen kann. Bei vielen Diensten, die in der Cloud laufen, kann sich der DNS dynamisch ändern. In diesem Fall gäbe es jedoch keine dynamische Anpassung, so dass Anfragen ins Leere laufen könnten. Mit der HttpClientFactory kann ein HttpClient erzeugt werden. Der HttpClient erzeugt jedoch keinen neuen HttpMessageHandler und keinen neuen SocketsHttpHandler, sondern greift auf einen Pool von Handlern zu und umgeht hierdurch das Problem mit nicht verfügbaren Sockets/Verbindungen. Die Handler werden dabei by default alle 240 Sekunden neu erzeugt, um das oben beschriebene DNS Problem zu vermeiden. Es folgen Beispiele, wie die HttpClientFactory Registrierung: Durch AddHttpClient steht das IHttpClientFactory Interface zur Verfügung Um einen HttpClient einen String als Namen zuzuweisen und zu verwenden, kann man folgendermaßen vorgehen. Hierdurch ist es einfacher den Client zu konfigurieren. Registrierung: Implementierung:
Inhaltsverzeichnis
Der HttpClient
Disposable HttpClient
1using (var client = new HttpClient())
2{
3 var response = await client.GetAsync(https://example.com);
4
5 if (response.IsSuccessStatusCode)
6 {
7 var content = await response.Content.ReadAsStringAsync();
8 // process the content
9 }
10}
1netstat -abn
2a: all connections
3b: processes
4n: addresses and port numbers
HttpClient als Singleton
Verwendung der HttpClientFactory
Hintergrund
Direkte Verwendung von HttpClientFactory
1public void ConfigureServices(IServiceCollection services)
2{
3 services.AddHttpClient();
4 // other services...
5}
1public class MyService
2{
3 private readonly HttpClient _client;
4
5 public MyService(IHttpClientFactory clientFactory)
6 {
7 _client = clientFactory.CreateClient();
8 }
9
10 public async Task<string> GetPageAsync(string url)
11 {
12 var response = await _client.GetAsync(url);
13 if (response.IsSuccessStatusCode)
14 {
15 return await response.Content.ReadAsStringAsync();
16 }
17 else
18 {
19 return "";
20 }
21 }
22}
‘Named’ HttpClient
1public void ConfigureServices(IServiceCollection services)
2{
3 services.AddHttpClient("FooClient", c =>
4 {
5 c.BaseAddress = new Uri(https://api.fooservice.com/);
6 c.DefaultRequestHeaders.Add("Accept", "application/vnd.foo.v3+json");
7 c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryFooExample");
8 });
9
10 // other services...
11}
12
13public class MyService
14{
15 private readonly HttpClient _client;
16
17 public MyService(IHttpClientFactory clientFactory)
18 {
19 _client = clientFactory.CreateClient("FooClient");
20 }
21
22 public async Task<string> GetFooUserAsync(string userName)
23 {
24 var response = await _client.GetAsync($"/users/{userName}");
25 if (response.IsSuccessStatusCode)
26 {
27 return await response.Content.ReadAsStringAsync();
28 }
29 else
30 {
31 return "";
32 }
33 }
34}
Typsierter HttpClient
1public void ConfigureServices(IServiceCollection services)
2{
3 services.AddHttpClient<FooService>();
4
5 // other services...
6}
1public void ConfigureServices(IServiceCollection services)
2{
3public class FooService
4{
5 public HttpClient Client { get; }
6
7 public FooService(HttpClient client)
8 {
9 client.BaseAddress = new Uri(https://api.fooservice.com/);
10 client.DefaultRequestHeaders.Add("Accept", "application/vnd.foo.v3+json");
11 client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryFooExample");
12
13 Client = client;
14 }
15
16 public async Task<string> GetGithubUserAsync(string userName)
17 {
18 var response = await Client.GetAsync($"/users/{userName}");
19 if (response.IsSuccessStatusCode)
20 {
21 return await response.Content.ReadAsStringAsync();
22 }
23 else
24 {
25 return "";
26 }
27 }
28}
29}