Aracılığıyla paylaş


Yaygın IHttpClientFactory kullanım sorunları

Bu makalede, kullanarak örnek oluştururken IHttpClientFactory HttpClient karşılaşabileceğiniz en yaygın sorunlardan bazılarını öğreneceksiniz.

IHttpClientFactory DI kapsayıcısında birden çok HttpClient yapılandırma ayarlamanın, günlüğe kaydetmeyi yapılandırmanın, dayanıklılık stratejilerini ayarlamanın ve daha fazlasının kullanışlı bir yoludur. IHttpClientFactoryayrıca yuva tükenmesi ve DNS değişikliklerini kaybetme gibi sorunları önlemek için ve HttpMessageHandler örneklerinin HttpClient yaşam süresi yönetimini kapsüller. .NET uygulamanızda nasıl kullanılacağına IHttpClientFactory genel bakış için bkz . .NET ile IHttpClientFactory.

DI ile tümleştirmenin IHttpClientFactory karmaşık yapısı nedeniyle, yakalaması ve gidermesi zor olabilecek bazı sorunlarla karşılaşabilirsiniz. Bu makalede listelenen senaryolar, olası sorunları önlemek için proaktif olarak uygulayabileceğiniz öneriler de içerir.

HttpClient yaşam süresine saygı Scoped göstermez

kapsamındaki herhangi bir hizmete(örneğin, HttpContext) veya bazı kapsamlı önbelleklere içinden HttpMessageHandlererişmeniz gerekiyorsa bir sorunla karşılaşırsınız. Oraya kaydedilen veriler "kaybolabilir" veya başka bir deyişle, "kalıcı" duruma gelebilir. Bunun nedeni uygulama bağlamı ile işleyici örneği arasındaki Bağımlılık Ekleme (DI) kapsamı uyuşmazlığıdır ve içinde IHttpClientFactorybilinen bir sınırlamadır.

IHttpClientFactory her HttpMessageHandler örnek için ayrı bir DI kapsamı oluşturur. Bu işleyici kapsamları uygulama bağlamı kapsamlarından (örneğin, ASP.NET Çekirdek gelen istek kapsamı veya kullanıcı tarafından oluşturulan bir el ile DI kapsamı) farklıdır, bu nedenle kapsamlı hizmet örneklerini paylaşmaz.

Bu sınırlamanın bir sonucu olarak:

  • Kapsamı belirlenmiş bir hizmette "harici olarak" önbelleğe alınan hiçbir veri içinde HttpMessageHandlerkullanılamaz.
  • veya kapsamındaki bağımlılıkları içinde HttpMessageHandler "dahili olarak" önbelleğe alınan tüm veriler, aynı işleyiciyi paylaşabilecekleri için birden çok uygulama DI kapsamından (örneğin, farklı gelen isteklerden) gözlemlenebilir.

Bu bilinen sınırlamayı hafifletmeye yardımcı olmak için aşağıdaki önerileri göz önünde bulundurun:

❌ Hassas bilgilerin sızmasını önlemek için kapsamla ilgili bilgileri (örneğin, içindeki veriler HttpContext) örneklerin veya bağımlılıklarının içinde HttpMessageHandler önbelleğe ALMAYIN.

❌ İşleyici ile paylaşılacağından CookieContainer tanımlama bilgilerini KULLANMAYIN.

✔️ Bilgileri depolamamayı veya yalnızca örnekte geçirmeyi HttpRequestMessage GÖZ ÖNÜNDE BULUNDURUN.

ile birlikte HttpRequestMessagerastgele bilgiler geçirmek için özelliğini kullanabilirsiniz HttpRequestMessage.Options .

✔️ Kapsamla ilgili tüm (örneğin, kimlik doğrulaması) mantığını , tarafından IHttpClientFactoryoluşturulmamış ayrı DelegatingHandler bir şekilde kapsülledikten sonra bunu kullanarak -created işleyicisini IHttpClientFactorysarmalayın.

olmadan yalnızca bir HttpMessageHandler oluşturmak için kayıtlı adlandırılmış herhangi bir istemciyi çağırIHttpMessageHandlerFactory.CreateHandler.HttpClient Bu durumda, birleştirilmiş işleyiciyi kullanarak kendiniz bir HttpClient örnek oluşturmanız gerekir. GitHub'da bu geçici çözüm için tam olarak çalıştırılabilir bir örnek bulabilirsiniz.

Daha fazla bilgi için yönergelerdeki IHttpClientFactory'de IHttpClientFactory İleti İşleyici Kapsamları bölümüne bakın.

HttpClient DNS değişikliklerine saygı göstermez

Kullanılsa IHttpClientFactory bile eski DNS sorununa isabet etmek mümkündür. Bu durum genellikle bir HttpClient örneğin bir Singleton hizmette yakalanması veya genel olarak belirtilenden HandlerLifetimedaha uzun bir süre boyunca bir yerde depolanması durumunda gerçekleşebilir. HttpClient ayrıca, ilgili yazılan istemci bir tekil tarafından yakalanırsa yakalanır.

❌tarafından IHttpClientFactory oluşturulan örnekleri uzun süreler boyunca önbelleğe HttpClient ALMAYIN.

❌Yazılan istemci örneklerini hizmetlere Singleton EKLEMEYİN.

✔️ Bir istemciden IHttpClientFactory zamanında veya her ihtiyaç duyduğunuzda istekte bulunmayı GÖZ ÖNÜNDE BULUNDURUN. Fabrika tarafından oluşturulan istemcilerin atılması güvenlidir.

HttpClienttarafından IHttpClientFactory oluşturulan örneklerin kısa ömürlü olması amaçlanmıştır.

  • İşleyicilerin DNS değişikliklerine tepki vermelerini sağlamak için IHttpClientFactory yaşam süreleri dolduğunda geri dönüşüm ve yeniden oluşturma HttpMessageHandlergereklidir. HttpClient oluşturulurken belirli bir işleyici örneğine bağlıdır, bu nedenle istemcinin güncelleştirilmiş işleyiciyi edineceğinden emin olmak için yeni HttpClient örneklerin zamanında istenmesi gerekir.

  • Fabrika tarafından oluşturulan bu tür HttpClient örneklerin atılması yuva tükenmesine yol açmaz, bunun atılması, 'nin atılması HttpMessageHandlertetiklenmez. IHttpClientFactoryörnekleri oluşturmak HttpClient için kullanılan kaynakları izler ve atar. Örneğin HttpMessageHandler kullanım ömrü dolmaya başlar ve kullanım süresi dolmaz.HttpClient

Yazılan istemcilerin de kısa ömürlü olması amaçlanır ve oluşturucuya bir HttpClient örnek eklendiğinden, yazılan istemci ömrünü paylaşır.

Daha fazla bilgi için yönergelerdeki HttpClient Tekli hizmetlerde yaşam süresi yönetimi ve Yazılan istemcilerden kaçınma bölümlerine IHttpClientFactory bakın.

HttpClient çok fazla yuva kullanıyor

Kullanılsa IHttpClientFactory bile, belirli bir kullanım senaryosunda yuva tükenme sorununa isabet etmek mümkündür. Varsayılan olarak, HttpClient eşzamanlı istek sayısını sınırlamaz. Aynı anda çok sayıda HTTP/1.1 isteği başlatılırsa, havuzda boş bağlantı olmadığından ve sınır ayarlanmadığından her biri yeni bir HTTP bağlantı girişimi tetikler.

❌ Sınırları belirtmeden aynı anda çok sayıda HTTP/1.1 isteği başlatmaYIN.

✔️ Bunu birincil işleyici olarak kullanıyorsanız, makul bir değere ayarlamayı HttpClientHandler.MaxConnectionsPerServer (veya SocketsHttpHandler.MaxConnectionsPerServer) GÖZ ÖNÜNDE BULUNDURUN. Bu sınırların yalnızca belirli işleyici örneği için geçerli olduğunu unutmayın.

✔️ Tek bir TCP bağlantısı üzerinden isteklerin birden çok katına alınmasına izin veren HTTP/2 kullanmayı göz önünde bulundurun.

Yazılan istemci yanlış HttpClient eklenmiş

Türlenmiş bir istemciye beklenmeyen HttpClient bir eklemenin mümkün olduğu çeşitli durumlar olabilir. Çoğu zaman, di tasarımına göre bir hizmetin sonraki kayıtları öncekini geçersiz kılacağından, kök neden hatalı bir yapılandırmada olur.

Yazılan istemciler adlandırılmış istemcileri "arka planda" kullanır: yazılan bir istemciyi örtük olarak kaydeder ve adlandırılmış bir istemciye bağlar. açıkça belirtilmediği sürece istemci adı türüne TClientayarlanır. Aşırı yüklemeler kullanılırsaAddHttpClient<TClient,TImplementation>, bu çiftten TClient,TImplementation ilk kez kullanılır.

Bu nedenle, türü belirlenmiş bir istemciyi kaydetmek iki ayrı işlem yapar:

  1. Adlandırılmış bir istemciyi kaydeder (basit bir varsayılan örnekte, adı olur typeof(TClient).Name).
  2. veya TClient,TImplementation öğesini kullanarak TClient bir Transient hizmeti kaydeder.

Aşağıdaki iki deyim teknik olarak aynıdır:

services.AddHttpClient<ExampleClient>(c => c.BaseAddress = new Uri("http://example.com"));

// -OR-

services.AddHttpClient(nameof(ExampleClient), c => c.BaseAddress = new Uri("http://example.com")) // register named client
    .AddTypedClient<ExampleClient>(); // link the named client to a typed client

Basit bir durumda, aşağıdakine benzer olacaktır:

services.AddHttpClient(nameof(ExampleClient), c => c.BaseAddress = new Uri("http://example.com")); // register named client

// register plain Transient service and link it to the named client
services.AddTransient<ExampleClient>(s =>
    new ExampleClient(
        s.GetRequiredService<IHttpClientFactory>().CreateClient(nameof(ExampleClient))));

Yazılan ve adlandırılmış istemciler arasındaki bağlantının nasıl bozulabileceğine ilişkin aşağıdaki örnekleri göz önünde bulundurun.

Yazılan istemci ikinci kez kaydedilir

❌Yazılan istemciyi ayrı kaydetmeYIN; zaten çağrı tarafından AddHttpClient<T> otomatik olarak kaydedilir.

Yazılan bir istemci yanlışlıkla düz Geçici hizmet olarak ikinci kez kaydedilirse, bu, tarafından HttpClientFactoryeklenen kaydın üzerine yazılır ve adlandırılmış istemcinin bağlantısını bozar. Yapılandırılmamış HttpClient bir istemci bunun yerine türü belirtilen istemciye eklendiğinden yapılandırması kaybolur gibi HttpClientbildirimde bulunur.

Özel durum oluşturmak yerine "yanlış" HttpClient kullanılması kafa karıştırıcı olabilir. Bunun nedeni, en temel HttpClientFactory kullanım senaryosunu etkinleştirmek için "varsayılan" yapılandırılmamış HttpClient olan (string.Empty) adlı istemcinin Options.DefaultName düz Geçici hizmet olarak kaydedilmesidir. Bu nedenle, bağlantı bozulur ve yazılan istemci sıradan bir hizmet haline geldikten sonra, bu "varsayılan" HttpClient doğal olarak ilgili oluşturucu parametresine eklenir.

Farklı türdeki istemciler ortak bir arabirime kaydedilir

İki farklı türe sahip istemcinin ortak bir arabirimde kayıtlı olması durumunda ikisi de aynı adlandırılmış istemciyi yeniden kullanabilir. Bu, ikinci adlandırılmış istemciyi "yanlış" olarak eklenen ilk yazılan istemci gibi görünebilir.

❌ Adı açıkça belirtmeden tek bir arabirimde birden çok türe bağlı istemci kaydetmeYİN.

✔️ Adlandırılmış istemciyi ayrı ayrı kaydetmeyi ve yapılandırmayı ve ardından çağrıda AddHttpClient<T> adı belirterek veya adlandırılmış istemci kurulumu sırasında arayarak AddTypedClient bunu bir veya birden çok türü belirlenmiş istemciye bağlamayı göz önünde bulundurun.

Tasarım gereği, aynı ada sahip adlandırılmış bir istemciyi birkaç kez kaydedip yapılandırmak, yapılandırma eylemlerini var olan istemciler listesine ekler. Bu davranışı HttpClientFactory belirgin olmayabilir, ancak Seçenekler deseni ve gibi Configureyapılandırma API'leri tarafından kullanılan yaklaşımla aynıdır.

Bu çoğunlukla gelişmiş işleyici yapılandırmaları için yararlıdır; örneğin, harici olarak tanımlanan adlandırılmış bir istemciye özel işleyici ekleme veya testler için birincil işleyiciyle alay etme, ancak örnek yapılandırması için HttpClient de çalışır. Örneğin, aşağıdaki üç örnek aynı şekilde yapılandırılmış bir HttpClient sonuçla sonuçlanır (hem hem de BaseAddress DefaultRequestHeaders ayarlanır):

// one configuration callback
services.AddHttpClient("example", c =>
    {
        c.BaseAddress = new Uri("http://example.com");
        c.DefaultRequestHeaders.UserAgent.ParseAdd("HttpClient/8.0");
    });

// -OR-

// two configuration callbacks
services.AddHttpClient("example", c => c.BaseAddress = new Uri("http://example.com"))
    .ConfigureHttpClient(c => c.DefaultRequestHeaders.UserAgent.ParseAdd("HttpClient/8.0"));

// -OR-

// two configuration callbacks in separate AddHttpClient calls
services.AddHttpClient("example", c => c.BaseAddress = new Uri("http://example.com"));
services.AddHttpClient("example")
    .ConfigureHttpClient(c => c.DefaultRequestHeaders.UserAgent.ParseAdd("HttpClient/8.0"));

Bu, türü belirlenmiş bir istemciyi zaten tanımlanmış adlandırılmış bir istemciye bağlamayı ve ayrıca birkaç türü belirlenmiş istemciyi tek bir adlandırılmış istemciye bağlamayı sağlar. Bir parametreye sahip name aşırı yüklemeler kullanıldığında daha belirgindir:

services.AddHttpClient("LogClient", c => c.BaseAddress = new Uri(LogServerAddress));

services.AddHttpClient<FooLogger>("LogClient");
services.AddHttpClient<BarLogger>("LogClient");

Adlandırılmış istemci yapılandırması sırasında çağrılarak AddTypedClient da aynı şey elde edilebilir:

services.AddHttpClient("LogClient", c => c.BaseAddress = new Uri(LogServerAddress))
    .AddTypedClient<FooLogger>()
    .AddTypedClient<BarLogger>();

Ancak, aynı adlandırılmış istemciyi yeniden kullanmak istemiyorsanız, ancak yine de istemcileri aynı arabirime kaydetmek istiyorsanız, bunlar için açıkça farklı adlar belirterek bunu yapabilirsiniz:

services.AddHttpClient<ITypedClient, ExampleClient>(nameof(ExampleClient),
    c => c.BaseAddress = new Uri("http://example.com"));
services.AddHttpClient<ITypedClient, GithubClient>(nameof(GithubClient),
    c => c.BaseAddress = new Uri("https://github.com"));

Ayrıca bkz.