Apakah HttpClient dan HttpClientHandler harus dibuang di antara permintaan?

334

System.Net.Http.HttpClient dan System.Net.Http.HttpClientHandler di .NET Framework 4.5 mengimplementasikan IDisposable (via System.Net.Http.HttpMessageInvoker ).

The usingdokumentasi pernyataan mengatakan:

Sebagai aturan, ketika Anda menggunakan objek IDisposable, Anda harus mendeklarasikan dan membuat instance dalam pernyataan menggunakan.

Jawaban ini menggunakan pola ini:

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();
}

Tetapi contoh-contoh yang paling terlihat dari Microsoft tidak memanggil Dispose()secara eksplisit atau implisit. Misalnya:

Dalam komentar pengumuman , seseorang bertanya kepada karyawan Microsoft:

Setelah memeriksa sampel Anda, saya melihat bahwa Anda tidak melakukan tindakan buang pada contoh HttpClient. Saya telah menggunakan semua contoh HttpClient dengan menggunakan pernyataan pada aplikasi saya dan saya pikir itu adalah cara yang benar sejak HttpClient mengimplementasikan antarmuka IDisposable. Apakah saya di jalan yang benar?

Jawabannya adalah:

Secara umum itu benar meskipun Anda harus berhati-hati dengan "menggunakan" dan async karena mereka tidak benar-benar bercampur. Net 4, Dalam. Net 4.5 Anda dapat menggunakan "menunggu" di dalam pernyataan "menggunakan".

Btw, Anda dapat menggunakan kembali HttpClient yang sama sebanyak yang Anda suka sehingga biasanya Anda tidak akan membuat / membuangnya setiap saat.

Paragraf kedua berlebihan untuk pertanyaan ini, yang tidak peduli berapa kali Anda dapat menggunakan contoh HttpClient, tetapi tentang apakah perlu untuk membuangnya setelah Anda tidak lagi membutuhkannya.

(Pembaruan: sebenarnya paragraf kedua adalah kunci untuk jawabannya, seperti yang disediakan di bawah ini oleh @DPeden.)

Jadi pertanyaan saya adalah:

  1. Apakah perlu, mengingat implementasi saat ini (.NET Framework 4.5), untuk memanggil Buang () pada instance HttpClient dan HttpClientHandler? Klarifikasi: oleh "perlu" Maksud saya jika ada konsekuensi negatif untuk tidak membuang, seperti kebocoran sumber daya atau risiko korupsi data.

  2. Jika tidak perlu, apakah itu akan menjadi "praktik yang baik" karena mereka menerapkan IDisposable?

  3. Jika perlu (atau disarankan), apakah kode ini yang disebutkan di atas mengimplementasikannya dengan aman (untuk .NET Framework 4.5)?

  4. Jika kelas-kelas ini tidak perlu memanggil Buang (), mengapa mereka diimplementasikan sebagai IDisposable?

  5. Jika mereka memerlukan, atau jika itu adalah praktik yang disarankan, apakah contoh Microsoft menyesatkan atau tidak aman?

Fernando Correia
sumber
2
@Damien_The_Unbeliever, terima kasih atas tanggapan Anda. Apakah Anda punya saran tentang bagaimana saya bisa mengklarifikasi pertanyaan? Saya ingin tahu apakah itu dapat menyebabkan masalah yang umumnya terkait dengan tidak membuang sumber daya, seperti kebocoran sumber daya dan korupsi data.
Fernando Correia
9
@Damien_The_Unbeliever: Tidak benar. Khususnya, penulis aliran harus cenderung memiliki perilaku yang benar.
Stephen Cleary
1
@StephenCleary - aspek apa yang Anda pikirkan? Tentu saja, Anda dapat mengunjungi Flushsatu demi satu setiap penulisan, dan selain ketidaknyamanan terus menahan sumber daya yang mendasari lebih lama dari yang diperlukan, apa yang tidak akan terjadi yang diperlukan untuk "perilaku yang benar"?
Damien_The_Unbeliever
1
Ini benar-benar salah: "Sebagai aturan, ketika Anda menggunakan objek IDisposable, Anda harus mendeklarasikan dan membuat instantiate dalam pernyataan menggunakan". Saya akan membaca dokumentasi tentang implementasi kelas IDisposable selalu sebelum memutuskan apakah saya harus menggunakan menggunakan untuk itu. Sebagai penulis perpustakaan tempat saya menerapkan IDisposable karena perlu mengeluarkan sumber daya yang tidak berubah, saya akan ngeri jika konsumen yang dibuat membuang sebuah instance setiap kali alih-alih menggunakan kembali instance yang sudah ada. Itu tidak berarti jangan membuang contoh pada akhirnya ..
markmnl
1
Saya telah mengirimkan PR ke microsoft untuk memperbarui dokumen mereka: github.com/dotnet/docs/pull/2470
markmnl

Jawaban:

259

Konsensus umum adalah bahwa Anda tidak (seharusnya tidak) perlu membuang HttpClient.

Banyak orang yang terlibat erat dalam cara kerjanya menyatakan hal ini.

Lihat posting blog Darrel Miller dan posting SO terkait: Perayapan HttpClient mengakibatkan kebocoran memori untuk referensi.

Saya juga sangat menyarankan Anda membaca bab HttpClient dari Merancang API Web Evolvable dengan ASP.NET untuk konteks tentang apa yang terjadi di bawah tenda, khususnya bagian "Siklus Hidup" yang dikutip di sini:

Meskipun HttpClient secara tidak langsung mengimplementasikan antarmuka IDisposable, penggunaan standar HttpClient adalah tidak membuangnya setelah setiap permintaan. Objek HttpClient dimaksudkan untuk hidup selama aplikasi Anda perlu membuat permintaan HTTP. Memiliki objek ada di beberapa permintaan memungkinkan tempat untuk menetapkan DefaultRequestHeaders dan mencegah Anda dari harus menentukan kembali hal-hal seperti CredentialCache dan CookieContainer pada setiap permintaan seperti yang diperlukan dengan HttpWebRequest.

Atau bahkan membuka DotPeek.

David Peden
sumber
64
Untuk mengklarifikasi jawaban Anda, apakah benar untuk mengatakan bahwa "Anda tidak perlu membuang HttpClient JIKA ANDA TAHAN DENGAN INSTAN UNTUK MENGGUNAKAN TI KEMUDIAN"? Misalnya, jika suatu metode dipanggil berulang kali dan membuat instance HttpClient baru (walaupun itu bukan pola yang disarankan dalam kebanyakan kasus), apakah masih benar untuk mengatakan bahwa metode ini tidak boleh membuang instance (yang tidak akan digunakan kembali)? Ini dapat menyebabkan ribuan kejadian yang tidak diinginkan. Dengan kata lain, bahwa Anda harus mencoba dan menggunakan kembali instance, tetapi jika Anda tidak menggunakan kembali, Anda sebaiknya membuangnya (untuk melepaskan koneksi)?
Fernando Correia
8
Saya pikir jawaban yang dimengerti tetapi frustasi adalah tergantung. Jika saya harus disematkan untuk memberikan saran umum yang bekerja di sebagian besar (saya tidak pernah mengatakan semua) kasus, saya akan menyarankan Anda menggunakan wadah IoC dan mendaftarkan contoh HttpClient sebagai singleton. Masa pakai instance kemudian akan dicakup dengan masa pakai wadah. Ini dapat dicakup pada tingkat aplikasi atau mungkin per permintaan dalam aplikasi web.
David Peden
25
@FernandoCorreia Ya. Jika karena alasan tertentu Anda berulang kali membuat dan menghancurkan instance HttpClient maka ya, Anda harus Buang itu. Saya tidak menyarankan untuk mengabaikan antarmuka IDisposable, hanya mencoba mendorong orang untuk menggunakan kembali contoh.
Darrel Miller
20
Hanya untuk menambah kepercayaan lebih lanjut untuk jawaban ini, saya berbicara dengan tim HttpClient hari ini dan mereka mengkonfirmasi bahwa HttpClient tidak dirancang untuk digunakan sesuai permintaan. Sebuah instance dari HttpClient harus tetap hidup sementara aplikasi klien terus berinteraksi dengan host tertentu.
Darrel Miller
19
@ David Mendaftar HttpClient sebagai singleton terdengar berbahaya bagi saya karena itu bisa berubah. Misalnya, tidak semua orang yang menugaskan ke Timeoutproperti akan saling menginjak-injak?
Jon-Eric
47

Jawaban saat ini agak membingungkan dan menyesatkan, dan mereka kehilangan beberapa implikasi DNS penting. Saya akan mencoba untuk meringkas di mana semuanya berdiri dengan jelas.

  1. Secara umum, sebagian besar IDisposableobjek idealnya harus dibuang ketika Anda selesai menggunakannya , terutama yang memiliki sumber daya OS yang dinamai / dibagikan . HttpClienttidak terkecuali, karena seperti ditunjukkan oleh Darrel Miller, ia mengalokasikan token pembatalan, dan badan permintaan / respons dapat berupa aliran yang tidak dikelola.
  2. Namun, praktik terbaik untuk HttpClient mengatakan Anda harus membuat satu contoh dan menggunakannya kembali sebanyak mungkin (menggunakan anggota aman thread-nya dalam skenario multi-threaded). Oleh karena itu, dalam sebagian besar skenario Anda tidak akan pernah membuangnya hanya karena Anda akan membutuhkannya setiap saat .
  3. Masalah dengan menggunakan kembali HttpClient yang sama "selamanya" adalah bahwa koneksi HTTP yang mendasarinya mungkin tetap terbuka terhadap IP yang semula diselesaikan DNS, terlepas dari perubahan DNS . Ini bisa menjadi masalah dalam skenario seperti penyebaran biru / hijau dan failover berbasis DNS . Ada berbagai pendekatan untuk menangani masalah ini, yang paling dapat diandalkan yang melibatkan server mengirimkan Connection:closeheader setelah perubahan DNS terjadi. Kemungkinan lain melibatkan daur ulang HttpClientpada sisi klien, baik secara berkala atau melalui beberapa mekanisme yang mempelajari tentang perubahan DNS. Lihat https://github.com/dotnet/corefx/issues/11224 untuk informasi lebih lanjut (saya sarankan membacanya dengan seksama sebelum secara membabi buta menggunakan kode yang disarankan dalam posting blog yang ditautkan).
Ohad Schneider
sumber
Saya membuangnya sepanjang waktu karena saya tidak dapat beralih proxy pada contoh;)
ed22
Jika Anda perlu membuang HttpClient untuk alasan apa pun, Anda harus menyimpan instance statis dari HttpMessageHandler, karena membuang satu-satunya penyebab masalah yang dikaitkan dengan membuang HttpClient. HttpClient memiliki kelebihan konstruktor yang memungkinkan Anda menentukan bahwa handler yang disediakan tidak boleh dibuang, dalam hal ini Anda dapat menggunakan kembali HttpMessageHandler dengan instance HttpClient lainnya.
Tom Lint
Anda harus berpegang pada HttpClient Anda, tetapi Anda dapat menggunakan sesuatu seperti System.Net.ServicePointManager.DnsRefreshTimeout = 3000; Ini berguna misalnya jika Anda menggunakan perangkat seluler yang sewaktu-waktu dapat beralih antara wifi dan 4G.
Johan Franzén
18

Dalam pemahaman saya, panggilan Dispose()hanya diperlukan ketika itu mengunci sumber daya yang Anda butuhkan nanti (seperti koneksi tertentu). Itu selalu disarankan untuk membebaskan sumber daya yang tidak lagi Anda gunakan, bahkan jika Anda tidak membutuhkannya lagi, hanya karena Anda biasanya tidak memegang sumber daya yang tidak Anda gunakan (pun intended).

Contoh Microsoft tidak salah, tentu saja. Semua sumber daya yang digunakan akan dirilis saat aplikasi keluar. Dan dalam kasus contoh itu, itu terjadi segera setelah HttpClientselesai digunakan. Dalam kasus serupa, panggilan secara eksplisit Dispose()agak berlebihan.

Tetapi, secara umum, ketika sebuah kelas mengimplementasikan IDisposable, pemahamannya adalah bahwa Anda harus Dispose()melakukan instansinya segera setelah Anda sepenuhnya siap dan mampu. Saya berpendapat ini terutama benar dalam kasus-kasus seperti di HttpClientmana tidak terdokumentasi secara eksplisit mengenai apakah sumber daya atau koneksi dipegang / dibuka. Dalam kasus di mana koneksi akan digunakan kembali [segera], Anda harus melupakannya Dipose()- Anda tidak "sepenuhnya siap" dalam kasus itu.

Lihat juga: IDisposable. Metode Buang dan Kapan menelepon Buang

svidgen
sumber
7
Ini seperti jika seseorang membawa pisang ke rumah Anda, memakannya, dan berdiri dengan kulitnya. Apa yang harus mereka lakukan dengan kulitnya? ... Jika mereka sedang keluar dari pintu dengan itu, biarkan mereka pergi. Jika mereka berkeliaran, suruh mereka membuangnya ke tempat sampah agar tidak bau.
svidgen
hanya untuk memperjelas jawaban ini, apakah Anda mengatakan, "tidak perlu membuang jika program akan berakhir tepat setelah Anda menggunakannya"? Dan Anda harus membuangnya jika program diharapkan untuk beberapa waktu melakukan hal-hal lain?
Fernando Correia
@FernandoCorreia Ya, kecuali saya lupa sesuatu, saya pikir itu prinsip yang aman. Namun pikirkanlah dalam setiap kasus. Jika Anda bekerja dengan koneksi, misalnya, Anda tidak ingin melakukannya Dispose()sebelum waktunya dan harus menyambung kembali beberapa detik kemudian jika koneksi yang ada dapat digunakan kembali. Demikian juga, Anda tidak ingin tidak perlu Dispose()gambar atau struktur lain yang Anda mungkin akhirnya harus membangun kembali dalam satu atau dua menit.
svidgen
Saya mengerti. Tetapi dalam kasus khusus HttpClient dan HttpClientHandler tentang pertanyaan ini, apakah mereka menahan sumber daya terbuka seperti koneksi HTTP? Jika itu yang terjadi, saya mungkin harus memikirkan kembali pola saya menggunakannya.
Fernando Correia
1
@DPeden Jawaban Anda sama sekali tidak bertentangan dengan saya. Catatan, saya katakan, Anda harus Buang () dari instansnya segera setelah Anda sepenuhnya siap dan mampu . Jika Anda berencana menggunakan instance lagi, Anda belum siap .
svidgen
9

Buang () panggilan kode di bawah ini, yang menutup koneksi yang dibuka oleh instance HttpClient. Kode dibuat dengan mendekompilasi dengan dotPeek.

HttpClientHandler.cs - Buang

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

Jika Anda tidak memanggil buang, maka ServicePointManager.MaxServicePointIdleTime, yang dijalankan oleh timer, akan menutup koneksi http. Standarnya adalah 100 detik.

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();
}

Jika Anda belum menyetel waktu idle ke tak terbatas maka tampaknya aman untuk tidak memanggil buang dan biarkan timer koneksi idle menendang dan menutup koneksi untuk Anda, meskipun akan lebih baik bagi Anda untuk memanggil buang ke dalam pernyataan menggunakan jika Anda tahu Anda selesai dengan instance HttpClient dan membebaskan sumber daya lebih cepat.

Timothy Gonzalez
sumber
1
Anda juga dapat melihat kode di github. github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/…
TamusJRoyce
8

Jawaban singkat: Tidak, pernyataan dalam jawaban yang saat ini diterima TIDAK akurat : "Konsensus umum adalah bahwa Anda tidak (seharusnya tidak) perlu membuang HttpClient".

Jawaban panjang : KEDUA pernyataan berikut ini benar dan dapat dicapai pada saat yang sama:

  1. "HttpClient dimaksudkan untuk dipakai satu kali dan digunakan kembali sepanjang umur aplikasi", dikutip dari dokumentasi resmi .
  2. Suatu IDisposablebenda seharusnya / direkomendasikan untuk dibuang.

Dan mereka TIDAK PERLU KONFLIK satu sama lain. Ini hanya masalah bagaimana Anda mengatur kode Anda untuk menggunakan kembali HttpClientDAN masih membuangnya dengan benar.

Bahkan jawaban lagi dikutip dari saya jawaban lain :

Ini bukan kebetulan melihat orang-orang di beberapa posting blog menyalahkan bagaimana HttpClient's IDisposableantarmuka membuat mereka cenderung menggunakan using (var client = new HttpClient()) {...}pola dan kemudian menyebabkan masalah socket handler kelelahan.

Saya percaya bahwa konsepsi tak terucap (salah?): "Sebuah objek IDisposable diharapkan berumur pendek" .

NAMUN, sementara itu terlihat seperti hal yang berumur pendek ketika kita menulis kode dengan gaya ini:

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

yang dokumentasi resmi pada IDisposable tidak pernah menyebutkan IDisposablebenda harus berumur pendek. Menurut definisi, IDisposable hanyalah sebuah mekanisme untuk memungkinkan Anda melepaskan sumber daya yang tidak dikelola. Tidak ada lagi. Dalam hal itu, Anda HARUS pada akhirnya memicu pembuangan, tetapi itu tidak mengharuskan Anda untuk melakukannya dalam waktu singkat.

Karena itu, tugas Anda adalah memilih dengan tepat kapan akan memicu pembuangan, berdasarkan kebutuhan siklus hidup objek nyata Anda. Tidak ada yang menghentikan Anda menggunakan IDisposable dengan cara yang tahan lama:

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();
        }
    }
}

Dengan pemahaman baru ini, sekarang kita meninjau kembali posting blog itu , kita dapat dengan jelas melihat bahwa "perbaikan" diinisialisasi HttpClientsekali tetapi tidak pernah membuangnya, itu sebabnya kita dapat melihat dari output netstatnya bahwa, koneksi tetap pada keadaan ESTABLISHED yang berarti ia memiliki TIDAK ditutup dengan benar. Jika ditutup, kondisinya akan menjadi TIME_WAIT sebagai gantinya. Dalam praktiknya, bukan masalah besar untuk membocorkan hanya satu koneksi yang terbuka setelah seluruh program Anda berakhir, dan poster blog masih melihat peningkatan kinerja setelah perbaikan; tapi tetap saja, itu secara konsep tidak benar untuk menyalahkan IDisposable dan memilih untuk TIDAK membuangnya.

RayLuo
sumber
terima kasih atas penjelasannya. Ini jelas memberi penerangan ke arah konsensus. Dari pendapat Anda, kapan menurut Anda pantas untuk menelepon HttpClient.Dispose?
Jeson Martajaya
@JesonMartajaya, buang ketika aplikasi Anda tidak perlu lagi menggunakan contoh httpClient. Anda mungkin berpikir saran semacam itu terdengar kabur, tetapi sebenarnya saran itu dapat dengan sempurna sejalan dengan siklus hidup HttpClient clientvariabel Anda , yang merupakan hal Pemrograman-101 yang mungkin sudah Anda lakukan. Anda bahkan mungkin masih dapat menggunakannya using (...) {...}. Misalnya, lihat contoh Hello World di dalam jawaban saya.
RayLuo
7

Karena belum ada yang menyebutkannya di sini, cara terbaik baru untuk mengelola HttpClient dan HttpClientHandler di .Net Core 2.1 menggunakan HttpClientFactory .

Ini memecahkan sebagian besar masalah dan gotcha yang disebutkan di atas dengan cara yang bersih dan mudah digunakan. Dari posting blog hebat Steve Gordon :

Tambahkan paket-paket berikut ke proyek .Net Core (2.1.1 atau lebih baru):

Microsoft.AspNetCore.All
Microsoft.Extensions.Http

Tambahkan ini ke Startup.cs:

services.AddHttpClient();

Suntikkan dan gunakan:

[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);
    }
}

Jelajahi serangkaian posting di blog Steve untuk mendapatkan lebih banyak fitur.

pcdev
sumber
4

Dalam kasus saya, saya membuat HttpClient di dalam metode yang benar-benar melakukan panggilan layanan. Sesuatu seperti:

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

Dalam peran pekerja Azure, setelah berulang kali memanggil metode ini (tanpa membuang HttpClient), akhirnya akan gagal dengan SocketException(upaya koneksi gagal).

Saya menjadikan HttpClient sebagai variabel instan (membuangnya di tingkat kelas) dan masalah itu hilang. Jadi saya akan mengatakan, ya, buang HttpClient, dengan asumsi aman (Anda tidak memiliki panggilan async yang luar biasa) untuk melakukannya.

David Faivre
sumber
Terima kasih untuk umpan baliknya. Ini adalah masalah yang agak rumit. Saya sarankan membaca artikel yang terhubung dalam jawaban DPeden. Singkatnya, instance HttpClient harus digunakan kembali di seluruh siklus hidup aplikasi. Jika Anda membuat instance baru berulang kali, Anda mungkin harus membuangnya.
Fernando Correia
6
"instance HttpClient harus digunakan kembali di seluruh siklus hidup aplikasi" ini bukan ide yang baik dengan banyak aplikasi. Saya sedang memikirkan aplikasi web yang menggunakan HttpClient. HttpClient memegang status (misalnya header permintaan yang akan digunakan), jadi satu utas permintaan web dapat dengan mudah menginjak-injak apa yang dilakukan orang lain. Dalam aplikasi web besar saya juga melihat HttpClient sebagai masalah untuk masalah koneksi utama. Ketika ragu saya katakan Buang.
Bytedev
@nashwan Anda tidak bisa menghapus header dan menambahkan yang baru sebelum setiap permintaan?
Mandeep Janjua
Microsoft juga merekomendasikan untuk menggunakan kembali contoh HttpClient
Mandeep Janjua
@MandeepJanjua contoh itu tampaknya menjadi klien sebagai aplikasi konsol. Saya merujuk pada aplikasi web sebagai klien.
bytedev
3

Dalam penggunaan umum (tanggapan <2GB) tidak perlu untuk Membuang HttpResponseMessages.

Jenis pengembalian metode HttpClient harus dibuang jika Konten Stream mereka tidak sepenuhnya dibaca. Kalau tidak, tidak ada cara bagi CLR untuk mengetahui Streaming tersebut dapat ditutup sampai mereka dikumpulkan.

  • Jika Anda membaca data menjadi byte [] (mis. GetByteArrayAsync) atau string, semua data dibaca, jadi tidak perlu dibuang.
  • Kelebihan lainnya akan default untuk membaca Stream hingga 2GB (HttpCompletionOption adalah ResponseContentRead, HttpClient.MaxResponseContentBufferSize default adalah 2GB)

Jika Anda mengatur HttpCompletionOption ke ResponseHeadersRead atau respons lebih besar dari 2GB, Anda harus membersihkan. Ini dapat dilakukan dengan memanggil Buang di HttpResponseMessage atau dengan menelepon Buang / Tutup di Stream yang diperoleh dari Konten HttpResonseMessage atau dengan membaca konten sepenuhnya.

Apakah Anda menelepon Buang di HttpClient tergantung pada apakah Anda ingin membatalkan permintaan yang tertunda atau tidak.

Tom Deseyn
sumber
2

Jika Anda ingin membuang HttpClient, Anda bisa jika mengaturnya sebagai kumpulan sumber daya. Dan pada akhir aplikasi Anda, Anda membuang sumber daya Anda.

Kode:

// 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 handler = HttpClientHander.GetHttpClientHandle (Uri baru ("base url")).

  • HttpClient, sebagai antarmuka, tidak dapat memanggil Buang ().
  • Buang () akan dipanggil secara tertunda oleh Pengumpul Sampah. Atau ketika program membersihkan objek melalui destruktornya.
  • Menggunakan Referensi Lemah + logika pembersihan tertunda sehingga tetap digunakan selama sering digunakan kembali.
  • Ini hanya mengalokasikan HttpClient baru untuk setiap URL basis yang diteruskan ke sana. Alasan yang dijelaskan oleh jawaban Ohad Schneider di bawah ini. Perilaku buruk saat mengubah url dasar.
  • HttpClientHandle memungkinkan untuk mengejek dalam tes
TamusJRoyce
sumber
Sempurna. Saya melihat bahwa Anda memanggil Disposemetode yang Anda daftarkan di GC. Ini harus dinilai lebih tinggi di atas.
Jeson Martajaya
Perhatikan bahwa HttpClient melakukan pengumpulan sumber daya per URL basis. Jadi, jika Anda memukul ribuan situs web yang berbeda dalam daftar, kinerja Anda akan menurun tanpa membersihkan masing-masing situs tersebut. Ini memperlihatkan kemampuan untuk membuang setiap url dasar. Namun, jika Anda hanya menggunakan satu situs web, mungkin hanya karena alasan akademis untuk menelepon buang.
TamusJRoyce
1

Menggunakan injeksi dependensi di konstruktor Anda membuat mengelola masa pakai Anda HttpClientlebih mudah - mengambil manajer seumur hidup di luar kode yang membutuhkannya dan membuatnya mudah diubah di kemudian hari.

Preferensi saya saat ini adalah membuat kelas klien http terpisah yang mewarisi dari HttpClientsatu kali per target titik akhir domain dan kemudian menjadikannya singleton menggunakan injeksi ketergantungan.public class ExampleHttpClient : HttpClient { ... }

Lalu saya mengambil ketergantungan konstruktor pada klien http khusus di kelas layanan tempat saya membutuhkan akses ke API itu. Ini memecahkan masalah seumur hidup dan memiliki keuntungan ketika datang ke koneksi pooling.

Anda dapat melihat contoh yang berfungsi dalam jawaban terkait di https://stackoverflow.com/a/50238944/3140853

alastairtree
sumber
-2

Saya pikir kita harus menggunakan pola singleton untuk menghindari keharusan membuat instance dari HttpClient dan menutupnya sepanjang waktu. Jika Anda menggunakan .Net 4.0 Anda bisa menggunakan kode sampel seperti di bawah ini. untuk informasi lebih lanjut tentang pola singleton, periksa di sini .

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

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

    private HttpClientSingletonWrapper()
    {
    }
}

Gunakan kode seperti di bawah ini.

var client = HttpClientSingletonWrapper.Instance;
yayadavid
sumber
3
Sesuatu yang harus diperhatikan ketika melakukan hal ini (dan skema serupa lainnya): " Setiap instance anggota tidak dijamin aman dari utas. "
tne
2
Apakah jawaban ini benar atau tidak, harus sepenuhnya bergantung pada apa aplikasi yang Anda ingin gunakan dari HttpClient. Jika Anda memiliki aplikasi web dan membuat HttpClient yang tunggal, maka semua permintaan web akan dibagikan dari Anda berpotensi akan mendapatkan banyak pengecualian koneksi (tergantung seberapa populer situs web Anda! :-)). (Lihat jawaban David
Faivre