HttpClient ve HttpClientHandler istekler arasında imha edilmeli mi?


334

.NET Framework 4.5'te System.Net.Http.HttpClient ve System.Net.Http.HttpClientHandler IDisposable'ı ( System.Net.Http.HttpMessageInvoker aracılığıyla ) uygular .

usingDeyimi belgelerine diyor ki:

Kural olarak, IDisposable nesnesini kullandığınızda, bunu bir using deyiminde bildirmeli ve başlatmalısınız.

Bu cevap şu kalıbı kullanır:

var baseAddress = new Uri("http://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("foo", "bar"),
        new KeyValuePair<string, string>("baz", "bazinga"),
    });
    cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value"));
    var result = client.PostAsync("/test", content).Result;
    result.EnsureSuccessStatusCode();
}

Ancak Microsoft'tan en görünür örnekler Dispose()açık ya da dolaylı olarak çağrılmaz . Örneğin:

Gelen duyuru 'yorumlarınıza s, birisi Microsoft çalışanı sordu:

Örneklerinizi kontrol ettikten sonra, HttpClient örneğinde atma işlemini yapmadığınızı gördüm. Uygulamamdaki ifadeyi kullanarak HttpClient'in tüm örneklerini kullandım ve HttpClient IDisposable arabirimini uyguladığı için doğru yol olduğunu düşündüm. Doğru yolda mıyım?

Cevabı şuydu:

Genel olarak doğrudur, ancak "kullanarak" ve async ile dikkatli olmak zorunda olmasına rağmen onlar gerçekten .Net 4, .Net 4.5 içinde bir "kullanma" deyimi içinde "beklemek" kullanabilirsiniz.

Btw, aynı HttpClient'i istediğiniz gibi tekrar kullanabilirsiniz, böylece genellikle her zaman oluşturmaz / atamazsınız.

İkinci paragraf, bir HttpClient örneğini kaç kez kullanabileceğinizle ilgili değil, artık ihtiyacınız kalmadan atmanızın gerekip gerekmediği ile ilgili olan bu soru için gereksizdir.

(Güncelleme: aslında, @DPeden tarafından sağlanan yanıtın anahtarı ikinci paragraftır.)

Yani sorularım:

  1. Geçerli uygulama (.NET Framework 4.5) göz önüne alındığında, HttpClient ve HttpClientHandler örneklerinde Dispose () öğesini çağırmak gerekli mi? Açıklama: "gerekli" ile, kaynak sızıntısı veya veri bozulması riskleri gibi bertaraf edilmemenin olumsuz sonuçları varsa kastediyorum.

  2. Gerekli değilse, IDisposable'ı uyguladıkları için zaten "iyi bir uygulama" olur mu?

  3. Gerekirse (veya önerilirse), yukarıda belirtilen bu kod güvenli bir şekilde uygulanıyor mu (.NET Framework 4.5 için)?

  4. Bu sınıflar Dispose () çağrılmasını gerektirmiyorsa, neden IDisposable olarak uygulandılar?

  5. Gerekiyorsa veya önerilen bir uygulamasa, Microsoft örnekleri yanıltıcı mı yoksa güvensiz mi?


2
@Damien_The_Unbeliever, geri bildiriminiz için teşekkür ederiz. Soruyu nasıl netleştirebileceğim konusunda herhangi bir öneriniz var mı? Genel olarak kaynak kaçağı ve veri bozulması gibi kaynakların atılmamasıyla ilgili sorunlara yol açıp açmayacağını bilmek istiyorum.
Fernando Correia

9
@Damien_The_Unbeliever: Doğru değil. Özellikle, akış yazarları doğru davranışa sahip olacak şekilde atılmalıdır.
Stephen Cleary

1
@StephenCleary - hangi yönleri düşünüyorsunuz? Kuşkusuz, Flushher yazma işleminden sonra birini arayabilir ve temel kaynakları gereğinden fazla tutmaya devam etmesinin zorluğunun dışında, "doğru davranış" için gerekli olmayan ne olmaz?
Damien_The_Unbeliever

1
Bu basit bir yanlıştır: "Kural olarak, IDisposable bir nesne kullandığınızda, bunu bir ifade kullanarak bildirmeli ve başlatmalısınız". Ben bunun için kullanarak kullanmanız gerekip gerekmediğine karar vermeden önce her zaman IDisposable uygulayan sınıf belgelerini okurdum. IDisposable uygulanabileceği kitaplıkların yazarı, yönetilmeyen kaynakları serbest bırakması gerektiğinden, tüketiciler var olan bir örneği yeniden kullanmak yerine her seferinde bir örnek atarlarsa dehşete düşerim. Yani nihayetinde örnek atmayın demek değildir ..
markmnl

1
Dokümanlarını güncellemek için microsoft'a bir PR gönderdim: github.com/dotnet/docs/pull/2470
markmnl

Yanıtlar:


259

Genel fikir birliği, HttpClient'i atmanız gerekmemesi (gerekmemesi) gerekliliğidir.

Çalışma biçimine yakından katılan birçok insan bunu belirtti.

Bkz Darrel Miller'in blog yazısı ve ilgili SO yazı: bellek sızıntısı HttpClient tarama sonuçlarınız referans için.

Ayrıca , özellikle burada belirtilen "Yaşam Döngüsü" bölümünde başlık altında neler olup bittiğine ilişkin bağlam için ASP.NET ile Evolvable Web API'leri Tasarlama bölümündeki HttpClient bölümünü okumanızı şiddetle tavsiye ederim :

HttpClient, IDisposable arabirimini dolaylı olarak uygulasa da, HttpClient'in standart kullanımı, her istekden sonra onu atmak değildir. HttpClient nesnesi, uygulamanızın HTTP istekleri yapması gerektiği sürece yaşaması amaçlanmıştır. Birden fazla istekte bir nesneye sahip olmak, DefaultRequestHeaders'ı ayarlamak için bir yer sağlar ve HttpWebRequest ile gereken her istekte CredentialCache ve CookieContainer gibi şeyleri yeniden belirtmenizi engeller.

Ya da DotPeek'i açın.


64
Cevabınızı açıklığa kavuşturmak için, "DAHA SONRA TEKRAR KULLANMAK İÇİN HOLDİNE BEKLİYORSANIZ HttpClient'i atmanıza gerek yok" demek doğru olur mu? Örneğin, bir yöntem tekrar tekrar çağrılırsa ve yeni bir HttpClient örneği oluşturursa (çoğu durumda önerilen model olmasa bile), bu yöntemin örneği atmaması gerektiğini (bu yeniden kullanılmayacaktır) söylemek doğru olur mu? Binlerce mevsimsiz örneğe yol açabilir. Başka bir deyişle, örnekleri yeniden kullanmayı denemeniz gerekir, ancak yeniden kullanmazsanız, onları atmanız daha iyi olur (bağlantıları serbest bırakmak için)?
Fernando Correia

8
Bence anlaşılır derecede sinir bozucu ama doğru cevap buna bağlı. Çoğu (asla söylemiyorum) vakalarda işe yarayan genel tavsiye vermek zorunda kalsaydım, bir IoC kabı kullanmanızı ve bir HttpClient örneğini singleton olarak kaydetmenizi öneririm. Daha sonra örneğin ömrü, konteynerin kullanım ömrünün kapsamına girer. Bu, uygulama düzeyinde veya bir web uygulamasındaki isteğe bağlı olarak belirlenebilir.
David Peden

25
@FernandoCorreia Evet. Herhangi bir nedenle tekrar tekrar HttpClient örnekleri oluşturup yok ederseniz, evet, Atmalısınız. IDisposable arayüzünü görmezden gelmeyi önermiyorum, sadece insanları örnekleri tekrar kullanmaya teşvik etmeye çalışıyorum.
Darrel Miller

20
Sadece bu cevaba daha fazla güven katmak için bugün HttpClient ekibiyle konuştum ve HttpClient'in istek başına kullanılmak üzere tasarlanmadığını doğruladılar. Bir istemci uygulaması belirli bir ana bilgisayarla etkileşime devam ederken, HttpClient örneği canlı tutulmalıdır.
Darrel Miller

19
@DavidPeden HttpClient'in singleton olarak kaydedilmesi benim için tehlikeli olduğu için tehlikeli geliyor. Örneğin, Timeoutmülke atanan herkes birbirinin üzerinde durmuyor mu?
Jon-Eric

47

Mevcut yanıtlar biraz kafa karıştırıcı ve yanıltıcıdır ve bazı önemli DNS uygulamalarından yoksundurlar. İşlerin nerede durduğunu özetlemeye çalışacağım.

  1. Genel olarak konuşursak, çoğu IDisposablenesne ideal olarak onlarla işiniz bittiğinde , özellikle Adlandırılmış / paylaşılan işletim sistemi kaynaklarına sahip olanlarda atılmalıdır . HttpClientbir istisna değildir, çünkü Darrel Miller'in işaret ettiği gibi, iptal belirteçleri tahsis eder ve istek / yanıt organları yönetilmeyen akışlar olabilir.
  2. Bununla birlikte, HttpClient için en iyi uygulama, bir örnek oluşturmanız ve bunu mümkün olduğunca tekrar kullanmanız gerektiğini ( çok iş parçacıklı senaryolarda iş parçacığı güvenli üyelerini kullanarak) söylüyor . Bu nedenle, çoğu senaryoda bunu asla atmayacaksınız çünkü her zaman ihtiyacınız olacak .
  3. Aynı HttpClient'i "sonsuza dek" kullanmayla ilgili sorun , temel HTTP bağlantısının DNS değişikliklerinden bağımsız olarak orijinal olarak DNS tarafından çözülmüş IP'ye karşı açık kalabilmesidir . Bu, mavi / yeşil dağıtım ve DNS tabanlı yük devretme gibi senaryolarda bir sorun olabilir . Bu sorunla başa çıkmak için çeşitli yaklaşımlar vardır, en güvenilir olanı Connection:closeDNS değişiklikleri gerçekleştikten sonra sunucunun bir başlık göndermesidir . Başka bir olasılık, HttpClientistemci tarafında periyodik olarak veya DNS değişikliğini öğrenen bir mekanizma aracılığıyla geri dönüştürülmesidir . Daha fazla bilgi için bkz. Https://github.com/dotnet/corefx/issues/11224 (Bağlantılı blog gönderisinde önerilen kodu körü körüne kullanmadan önce dikkatlice okumanızı öneririz).

Bir örnek üzerinde proxy değiştiremediğim için her zaman elden
çıkarırım

Herhangi bir nedenle HttpClient'i atmanız gerekiyorsa, HttpClient'i atmakla ilişkilendirilen sorunların nedenini ortadan kaldırmak için HttpMessageHandler'ın statik bir örneğini çevresinde tutmalısınız. HttpClient, sağlanan işleyicinin atılmaması gerektiğini belirtmenize izin veren bir yapıcı aşırı yüküne sahiptir, bu durumda HttpMessageHandler'ı diğer HttpClient örnekleriyle yeniden kullanabilirsiniz.
Tom Lint

HttpClient'inize tutunmalısınız, ancak System.Net.ServicePointManager.DnsRefreshTimeout = 3000 gibi bir şey kullanabilirsiniz; Bu, örneğin herhangi bir zamanda wifi ve 4G arasında geçiş yapabilen bir mobil cihazdaysanız kullanışlıdır.
Johan Franzén

18

Anladığım kadarıyla, arama Dispose()yalnızca daha sonra ihtiyacınız olan kaynakları kilitlediğinde (belirli bir bağlantı gibi) gereklidir. Tekrar kullanmanıza gerek kalmasa bile, artık kullanmadığınız kaynakların serbest bırakılması her zaman önerilir , çünkü genellikle kullanmadığınız kaynaklara bağlı kalmamalısınız (pun amaçlı).

Microsoft örneği mutlaka yanlış değildir. Uygulamadan çıkıldığında kullanılan tüm kaynaklar serbest bırakılacaktır. Ve bu örnek durumunda, bu, HttpClientkullanıldıktan hemen sonra gerçekleşir. Bu gibi durumlarda, açıkça arama Dispose()biraz gereksizdir.

Ancak, genel olarak, bir sınıf uygulandığında IDisposable, anlayış, Dispose()hazır olduğunuzda ve mümkün olan en kısa sürede örneklerinin olması gerektiğidir . Bu, özellikle HttpClientkaynakların veya bağlantıların açık / kapalı tutulup tutulmadığı konusunda açık bir şekilde belgelenmediği durumlarda doğrudur . Bağlantının [yakında] tekrar kullanılacağı durumda, bağlantıdan çıkmak isteyeceksiniz Dipose()- bu durumda "tam olarak hazır değilsiniz".

Ayrıca bkz: IDisposable.Dispose Yöntemi ve Ne zaman Çağırabilirsiniz?


7
Sanki birisi evinize bir muz getiriyor, onu yiyor ve kabuğuyla duruyormuş gibi. Kabuğuyla ne yapmalılar? ... Eğer kapıdan dışarı çıkarsa, bırak gitsinler. Eğer etrafta yapışıyorlarsa, onları çöp kutusuna atmalarını sağlayın, böylece yeri kıstırmaz.
svidgen

sadece bu cevabı açıklığa kavuşturmak için, "programı kullandıktan hemen sonra sona erecekse imha etmek gerekli değil" mi diyorsunuz? Ve programın bir süre başka şeyler yapmaya devam etmesi bekleniyorsa imha etmeniz gerekir mi?
Fernando Correia

@FernandoCorreia Evet, bir şey unutmadıkça güvenli bir ilke olduğunu düşünüyorum. Yine de her durumda biraz düşünün. Eğer bir bağlantı ile çalışıyorsanız, mesela, istemediğiniz Dispose()zamanından önce bunu ve daha sonra birkaç saniye yeniden zorunda olmadığını , mevcut bağlantı tekrar kullanılabilir. Benzer şekilde, gereksiz yere Dispose()bir veya iki dakika içinde yeniden inşa etmek zorunda kalacağınız görüntüleri veya diğer yapıları gereksiz yere istemezsiniz .
svidgen

Anlıyorum. Ancak bu sorunun söz konusu olduğu HttpClient ve HttpClientHandler durumlarında, HTTP bağlantısı gibi bir kaynağı açık tutuyorlar mı? Eğer olan buysa, onları kullanma şeklimi yeniden düşünmek zorunda kalabilirim.
Fernando Correia

1
@DPeden Cevabınız benimkiyle çatışmıyor. Not dedim, en kısa sürede konum olarak () onun örneklerinin imha etmelidir tamamen hazır ve muktedir . Örneği tekrar kullanmayı planlıyorsanız, hazır değilsiniz .
svidgen

9

Dispose () yöntemi, HttpClient örneği tarafından açılan bağlantıları kapatan aşağıdaki kodu çağırır. Kod, dotPeek ile ayrıştırılarak oluşturuldu.

HttpClientHandler.cs - Atma

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

Dispose öğesini çağırmazsanız, bir zamanlayıcı ile çalışan ServicePointManager.MaxServicePointIdleTime, http bağlantılarını kapatır. Varsayılan değer 100 saniyedir.

ServicePointManager.cs

internal static readonly TimerThread.Callback s_IdleServicePointTimeoutDelegate = new TimerThread.Callback(ServicePointManager.IdleServicePointTimeoutCallback);
private static volatile TimerThread.Queue s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(100000);

private static void IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)
{
  ServicePoint servicePoint = (ServicePoint) context;
  if (Logging.On)
    Logging.PrintInfo(Logging.Web, SR.GetString("net_log_closed_idle", (object) "ServicePoint", (object) servicePoint.GetHashCode()));
  lock (ServicePointManager.s_ServicePointTable)
    ServicePointManager.s_ServicePointTable.Remove((object) servicePoint.LookupString);
  servicePoint.ReleaseAllConnectionGroups();
}

Boşta kalma süresini sonsuz olarak ayarlamadıysanız, dispose çağırma ve boşta kalma zamanlayıcısının devreye girmesine ve bağlantıların sizin için kapatılmasına izin vermemek güvenli görünüyor, ancak bir kullanım deyiminde dispose çağrısı yapmanız daha iyi olacaktır. bir HttpClient örneği ile işiniz bittiğini bilirsiniz ve kaynakları daha hızlı boşaltabilirsiniz.



8

Kısa cevap: Hayır, şu anda kabul edilen cevaptaki ifade doğru DEĞİLDİR : "Genel fikir birliği, HttpClient'i atmanıza gerek olmamasıdır (gerekmemelidir)".

Uzun cevap : Aşağıdaki ifadelerin her ikisi de aynı anda doğru ve gerçekleştirilebilir:

  1. Resmi belgelerden alıntılanan "HttpClient'in bir kez başlatılması ve başvurunun ömrü boyunca yeniden kullanılması amaçlanmıştır" .
  2. Bir IDisposablenesnenin atılması gerekiyor / öneriliyor.

Ve GEREKLİ OLARAK ÇATIŞMAYIYORLAR. Bu sadece bir HttpClientVEYA kullanmaya devam etmek için kodunuzu nasıl organize ettiğinizle ilgilidir .

Başka bir cevabımdan alıntılanan daha uzun bir cevap :

İnsanları görmek için bir tesadüf değildir , bazı blog gönderileri nasıl suçlamaya HttpClientbireyin IDisposablearayüz onları kullanma eğiliminde yapar using (var client = new HttpClient()) {...}desen ve sonra bitkin soket işleyici soruna yol açar.

Bunun sözsüz (yanlış?) Bir anlayışa geldiğine inanıyorum: "Tek kullanımlık bir nesnenin kısa ömürlü olması bekleniyor" .

ANCAK, bu tarzda kod yazarken kesinlikle kısa ömürlü bir şey gibi görünüyor:

using (var foo = new SomeDisposableObject())
{
    ...
}

Tek kullanımlık nesneler hakkında resmi belgelerin hiçbir IDisposablezaman kısa ömürlü olması gerekmemektedir. Tanım gereği, IDisposable sadece yönetilmeyen kaynakları serbest bırakmanıza izin veren bir mekanizmadır. Başka bir şey yok. Bu anlamda, nihayetinde imhayı tetiklemeniz beklenir, ancak bunu kısa ömürlü bir şekilde yapmanızı gerektirmez.

Bu nedenle, elden çıkarmanın ne zaman tetikleneceğini, gerçek nesnenizin yaşam döngüsü gereksinimini temel alarak doğru bir şekilde seçmek sizin işinizdir. Uzun ömürlü bir şekilde ID'leri kullanmanıza engel olan hiçbir şey yoktur:

using System;
namespace HelloWorld
{
    class Hello
    {
        static void Main()
        {
            Console.WriteLine("Hello World!");

            using (var client = new HttpClient())
            {
                for (...) { ... }  // A really long loop

                // Or you may even somehow start a daemon here

            }

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

Bu yeni anlayışla, şimdi blog yayınını tekrar ziyaret ediyoruz, "düzeltmenin" bir HttpClientkez başlatıldığını ancak asla atmadığını açıkça görebiliyoruz, bu yüzden netstat çıktısından, bağlantının KURULDU durumunda kaldığını görebiliyoruz, yani Düzgün kapatılmamış. Kapatılsaydı, durumu TIME_WAIT olur. Uygulamada, tüm programınız bittikten sonra sadece bir bağlantıyı sızdırmak büyük bir sorun değildir ve blog posteri düzeltmeden sonra hala bir performans kazancı görmektedir; ama yine de, tek kullanımlık suçlamak ve atmamayı seçmek kavramsal olarak yanlıştır.


bu açıklama için teşekkürler. Açık bir şekilde fikir birliğine ışık tutuyor. Sizce, ne zaman aramanın uygun olduğunu düşünüyorsunuz HttpClient.Dispose?
Jeson Martajaya

@JesonMartajaya, uygulamanız artık httpClient örneğini kullanması gerekmediğinde atın. Böyle bir önerinin kulağa belirsiz geldiğini düşünebilirsiniz, ama aslında HttpClient clientdeğişkeninizin yaşam döngüsü ile mükemmel bir şekilde uyum sağlayabilir , ki bu zaten zaten yaptığınız bir Programlama-101 şeyidir. Hatta yine de kullanabilirsiniz using (...) {...}. Örneğin, cevabımın içindeki Merhaba Dünya örneğine bakın.
RayLuo

7

O kimse burada henüz söz ettiğini görünmüyor beri yeni en iyi yolu kullanıyor Net Çekirdek 2.1'de HttpClient ve HttpClientHandler yönetmek HttpClientFactory .

Yukarıda bahsedilen sorunların çoğunu ve gotcha'ları temiz ve kullanımı kolay bir şekilde çözer. Gönderen Steve Gordon'un büyük bir blog post :

.Net Core (2.1.1 veya üstü) projenize aşağıdaki paketleri ekleyin:

Microsoft.AspNetCore.All
Microsoft.Extensions.Http

Bunu Startup.cs dosyasına ekleyin:

services.AddHttpClient();

Enjekte edin ve kullanın:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public ValuesController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<ActionResult> Get()
    {
        var client = _httpClientFactory.CreateClient();
        var result = await client.GetStringAsync("http://www.google.com");
        return Ok(result);
    }
}

Daha fazla özellik için Steve'in blogundaki yayın dizisini keşfedin.


4

Benim durumumda, aslında servis çağrısı yapan bir yöntemin içinde bir HttpClient oluşturuyordum. Gibi bir şey:

public void DoServiceCall() {
  var client = new HttpClient();
  await client.PostAsync();
}

Bir Azure çalışan rolünde, bu yöntemi tekrar tekrar çağırdıktan sonra (HttpClient'i atmadan), sonunda başarısız olur SocketException(bağlantı girişimi başarısız oldu).

Ben HttpClient bir örnek değişken (sınıf düzeyinde bertaraf) yaptı ve sorun gitti. Evet diyebilirim ki, HttpClient'i, güvenli olduğunu (olağanüstü async çağrılarınız yok) varsayarak atın.


Geri dönüşünüz için teşekkür ederiz. Bu biraz karmaşık bir konudur. DPeden'in cevabında yer alan makaleleri okumanızı tavsiye ederim. Kısacası, HttpClient örneği uygulama yaşam döngüsü boyunca yeniden kullanılmalıdır. Tekrar tekrar yeni örnekler oluşturursanız, bunları atmanız gerekebilir.
Fernando Correia

6
"HttpClient örneği uygulama yaşam döngüsü boyunca yeniden kullanılmalıdır" Bu sadece bir çok uygulama ile iyi bir fikir değil. HttpClient kullanan web uygulamaları düşünüyorum. HttpClient durumu tutar (örneğin kullanacağı istek başlıkları), böylece bir web isteği iş parçacığı diğerinin ne yaptığını kolayca ezebilir. Büyük web uygulamalarında da HttpClient'i büyük bağlantı problemlerinde sorun olarak gördüm. Şüphe duyduğunuzda Dispose diyorum.
bytedev

@nashwan her istekten önce başlıkları temizleyip yenilerini ekleyemiyor musunuz?
Mandeep Janjua

Microsoft ayrıca HttpClient örneklerini yeniden kullanmanızı önerir - docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/…
Mandeep Janjua

@MandeepJanjua bu örnek bir konsol uygulaması olarak istemci gibi görünüyor. İstemci olan bir web uygulamasından bahsediyordum.
bytedev

3

Tipik kullanımda (<2GB yanıtları) HttpResponseMessage'ları Atmak gerekli değildir.

Akış İçeriği tam olarak Okunmuyorsa, HttpClient yöntemlerinin dönüş türleri Atılmalıdır. Aksi takdirde CLR'nin bu Akışların çöp toplanana kadar kapatılabileceğini bilmesi mümkün değildir.

  • Verileri bir bayt [] (örneğin GetByteArrayAsync) veya dizeye okuyorsanız, tüm veriler okunur, bu nedenle atmaya gerek yoktur.
  • Diğer aşırı yükler varsayılan olarak Akışı 2 GB'a kadar okuyacaktır (HttpCompletionOption, ResponseContentRead, HttpClient.MaxResponseContentBufferSize varsayılan değeri 2GB'dir)

HttpCompletionOption öğesini ResponseHeadersRead olarak ayarlarsanız veya yanıt 2 GB'den büyükse, temizlemeniz gerekir. Bu, HttpResponseMessage'da Dispose çağrılarak veya HttpResonseMessage İçeriğinden elde edilen Akış üzerinde Dispose / Close çağrılarak veya içeriği tamamen okunarak yapılabilir.

HttpClient üzerinde Dispose öğesini çağırıp çağırmamanız, bekleyen istekleri iptal etmek isteyip istemediğinize bağlıdır.


2

HttpClient'i atmak istiyorsanız, bunu bir kaynak havuzu olarak ayarladıysanız yapabilirsiniz. Ve uygulamanızın sonunda kaynak havuzunuzu elden çıkarırsınız.

Kod:

// Notice that IDisposable is not implemented here!
public interface HttpClientHandle
{
    HttpRequestHeaders DefaultRequestHeaders { get; }
    Uri BaseAddress { get; set; }
    // ...
    // All the other methods from peeking at HttpClient
}

public class HttpClientHander : HttpClient, HttpClientHandle, IDisposable
{
    public static ConditionalWeakTable<Uri, HttpClientHander> _httpClientsPool;
    public static HashSet<Uri> _uris;

    static HttpClientHander()
    {
        _httpClientsPool = new ConditionalWeakTable<Uri, HttpClientHander>();
        _uris = new HashSet<Uri>();
        SetupGlobalPoolFinalizer();
    }

    private DateTime _delayFinalization = DateTime.MinValue;
    private bool _isDisposed = false;

    public static HttpClientHandle GetHttpClientHandle(Uri baseUrl)
    {
        HttpClientHander httpClient = _httpClientsPool.GetOrCreateValue(baseUrl);
        _uris.Add(baseUrl);
        httpClient._delayFinalization = DateTime.MinValue;
        httpClient.BaseAddress = baseUrl;

        return httpClient;
    }

    void IDisposable.Dispose()
    {
        _isDisposed = true;
        GC.SuppressFinalize(this);

        base.Dispose();
    }

    ~HttpClientHander()
    {
        if (_delayFinalization == DateTime.MinValue)
            _delayFinalization = DateTime.UtcNow;
        if (DateTime.UtcNow.Subtract(_delayFinalization) < base.Timeout)
            GC.ReRegisterForFinalize(this);
    }

    private static void SetupGlobalPoolFinalizer()
    {
        AppDomain.CurrentDomain.ProcessExit +=
            (sender, eventArgs) => { FinalizeGlobalPool(); };
    }

    private static void FinalizeGlobalPool()
    {
        foreach (var key in _uris)
        {
            HttpClientHander value = null;
            if (_httpClientsPool.TryGetValue(key, out value))
                try { value.Dispose(); } catch { }
        }

        _uris.Clear();
        _httpClientsPool = null;
    }
}

var işleyici = HttpClientHander.GetHttpClientHandle (yeni Uri ("temel url")).

  • HttpClient, arayüz olarak Dispose () öğesini çağıramaz.
  • Çöp Toplayıcı tarafından Dispose () gecikmeli olarak çağrılır. Veya program nesneyi yıkıcısıyla temizlediğinde.
  • Zayıf Referanslar + gecikmeli temizleme mantığını kullanır, böylece sık sık tekrar kullanıldığı sürece kullanımda kalır.
  • Yalnızca kendisine iletilen her temel URL için yeni bir HttpClient ayırır. Ohad Schneider tarafından açıklanan nedenler aşağıdadır. Temel URL değiştirilirken kötü davranış.
  • HttpClientHandle testlerde alay etmeyi sağlar

Mükemmel. DisposeGC'ye kaydettiğiniz yöntemi çağırdığınızı görüyorum . Bu en üstte derecelendirilmelidir.
Jeson Martajaya

HttpClient'in temel URL başına kaynak havuzu oluşturduğunu unutmayın. Dolayısıyla, bir listede binlerce farklı web sitesini ziyaret ediyorsanız, performansınız bu siteleri temizlemeden düşecektir. Bu, her bir temel URL'yi atma yeteneğini ortaya koyar. Ancak, yalnızca bir web sitesi kullanıyorsanız, imha etme olarak adlandırmanız yalnızca akademik nedenlerle olabilir.
TamusJRoyce

1

Oluşturucunuzda bağımlılık enjeksiyonu kullanmak, ömür boyu yöneticiyi HttpClientihtiyaç duyduğu kodun dışına çıkarmak ve daha sonraki bir tarihte kolayca değiştirilebilmesini sağlamak için kullanım ömrünüzü yönetmenizi kolaylaştırır.

Şu anki tercihim, hedef uç nokta etki alanı başına bir kez miras kalan ayrı bir http istemci sınıfı oluşturmakHttpClient ve daha sonra bağımlılık enjeksiyonu kullanarak tek birton yapmaktır.public class ExampleHttpClient : HttpClient { ... }

Sonra ben bu API erişimi gereken hizmet sınıflarında özel http istemcisine bir yapıcı bağımlılığı almak. Bu, ömür boyu problemi çözer ve bağlantı havuzu oluşturma konusunda avantajlara sahiptir.

Çalışılan bir örneği ilgili cevapta https://stackoverflow.com/a/50238944/3140853 adresinde görebilirsiniz.



-2

Ben bir HttpClient örnekleri oluşturmak ve her zaman kapatmak zorunda tekil desen kullanmak gerektiğini düşünüyorum. Net 4.0 kullanıyorsanız aşağıdaki gibi bir örnek kod kullanabilirsiniz. singleton paterni hakkında daha fazla bilgi için burayı kontrol edin .

class HttpClientSingletonWrapper : HttpClient
{
    private static readonly Lazy<HttpClientSingletonWrapper> Lazy= new Lazy<HttpClientSingletonWrapper>(()=>new HttpClientSingletonWrapper()); 

    public static HttpClientSingletonWrapper Instance {get { return Lazy.Value; }}

    private HttpClientSingletonWrapper()
    {
    }
}

Kodu aşağıdaki gibi kullanın.

var client = HttpClientSingletonWrapper.Instance;

3
Bunu yaparken dikkat edilmesi gereken bir şey (ve diğer benzer şemalar): " Herhangi bir örnek üyesinin iş parçacığı için güvenli olduğu garanti edilmez. "
tne

2
Bu cevabın doğru olup olmadığı, HttpClient'i kullanmak istediğiniz uygulamanın ne olduğuna tamamen bağlı olmalıdır. Bir web uygulamanız varsa ve tek bir HttpClient oluşturursanız, tüm web isteklerinin paylaşacağınız potansiyel olarak çok fazla bağlantı istisnası elde edersiniz (web sitenizin ne kadar popüler olduğuna bağlı olarak! :-)). (David
Faivre'nin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.