System.Net.Http.HttpClient dan System.Net.Http.HttpClientHandler di .NET Framework 4.5 mengimplementasikan IDisposable (via System.Net.Http.HttpMessageInvoker ).
The using
dokumentasi 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:
- The artikel blog asli mengumumkan relase dari HttpClient.
- Dokumentasi MSDN sebenarnya untuk HttpClient.
- BingTranslateSample
- GoogleMapsSample
- WorldBankSample
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:
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.
Jika tidak perlu, apakah itu akan menjadi "praktik yang baik" karena mereka menerapkan IDisposable?
Jika perlu (atau disarankan), apakah kode ini yang disebutkan di atas mengimplementasikannya dengan aman (untuk .NET Framework 4.5)?
Jika kelas-kelas ini tidak perlu memanggil Buang (), mengapa mereka diimplementasikan sebagai IDisposable?
Jika mereka memerlukan, atau jika itu adalah praktik yang disarankan, apakah contoh Microsoft menyesatkan atau tidak aman?
sumber
Flush
satu 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"?Jawaban:
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:
Atau bahkan membuka DotPeek.
sumber
Timeout
properti akan saling menginjak-injak?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.
IDisposable
objek idealnya harus dibuang ketika Anda selesai menggunakannya , terutama yang memiliki sumber daya OS yang dinamai / dibagikan .HttpClient
tidak terkecuali, karena seperti ditunjukkan oleh Darrel Miller, ia mengalokasikan token pembatalan, dan badan permintaan / respons dapat berupa aliran yang tidak dikelola.Connection:close
header setelah perubahan DNS terjadi. Kemungkinan lain melibatkan daur ulangHttpClient
pada 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).sumber
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
HttpClient
selesai digunakan. Dalam kasus serupa, panggilan secara eksplisitDispose()
agak berlebihan.Tetapi, secara umum, ketika sebuah kelas mengimplementasikan
IDisposable
, pemahamannya adalah bahwa Anda harusDispose()
melakukan instansinya segera setelah Anda sepenuhnya siap dan mampu. Saya berpendapat ini terutama benar dalam kasus-kasus seperti diHttpClient
mana tidak terdokumentasi secara eksplisit mengenai apakah sumber daya atau koneksi dipegang / dibuka. Dalam kasus di mana koneksi akan digunakan kembali [segera], Anda harus melupakannyaDipose()
- Anda tidak "sepenuhnya siap" dalam kasus itu.Lihat juga: IDisposable. Metode Buang dan Kapan menelepon Buang
sumber
Dispose()
sebelum waktunya dan harus menyambung kembali beberapa detik kemudian jika koneksi yang ada dapat digunakan kembali. Demikian juga, Anda tidak ingin tidak perluDispose()
gambar atau struktur lain yang Anda mungkin akhirnya harus membangun kembali dalam satu atau dua menit.Buang () panggilan kode di bawah ini, yang menutup koneksi yang dibuka oleh instance HttpClient. Kode dibuat dengan mendekompilasi dengan dotPeek.
HttpClientHandler.cs - Buang
Jika Anda tidak memanggil buang, maka ServicePointManager.MaxServicePointIdleTime, yang dijalankan oleh timer, akan menutup koneksi http. Standarnya adalah 100 detik.
ServicePointManager.cs
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.
sumber
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:
IDisposable
benda seharusnya / direkomendasikan untuk dibuang.Dan mereka TIDAK PERLU KONFLIK satu sama lain. Ini hanya masalah bagaimana Anda mengatur kode Anda untuk menggunakan kembali
HttpClient
DAN 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
'sIDisposable
antarmuka membuat mereka cenderung menggunakanusing (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:
yang dokumentasi resmi pada IDisposable tidak pernah menyebutkan
IDisposable
benda 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:
Dengan pemahaman baru ini, sekarang kita meninjau kembali posting blog itu , kita dapat dengan jelas melihat bahwa "perbaikan" diinisialisasi
HttpClient
sekali 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.sumber
HttpClient.Dispose
?HttpClient client
variabel Anda , yang merupakan hal Pemrograman-101 yang mungkin sudah Anda lakukan. Anda bahkan mungkin masih dapat menggunakannyausing (...) {...}
. Misalnya, lihat contoh Hello World di dalam jawaban saya.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):
Tambahkan ini ke Startup.cs:
Suntikkan dan gunakan:
Jelajahi serangkaian posting di blog Steve untuk mendapatkan lebih banyak fitur.
sumber
Dalam kasus saya, saya membuat HttpClient di dalam metode yang benar-benar melakukan panggilan layanan. Sesuatu seperti:
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.
sumber
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 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.
sumber
Jika Anda ingin membuang HttpClient, Anda bisa jika mengaturnya sebagai kumpulan sumber daya. Dan pada akhir aplikasi Anda, Anda membuang sumber daya Anda.
Kode:
var handler = HttpClientHander.GetHttpClientHandle (Uri baru ("base url")).
sumber
Dispose
metode yang Anda daftarkan di GC. Ini harus dinilai lebih tinggi di atas.Menggunakan injeksi dependensi di konstruktor Anda membuat mengelola masa pakai Anda
HttpClient
lebih 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
HttpClient
satu 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
sumber
Silakan baca jawaban saya untuk pertanyaan yang sangat mirip yang diposting di bawah ini. Harus jelas bahwa Anda harus memperlakukan
HttpClient
instance sebagai lajang dan digunakan kembali di seluruh permintaan.Apa overhead untuk membuat HttpClient baru per panggilan dalam klien WebAPI?
sumber
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 .
Gunakan kode seperti di bawah ini.
sumber