Baru-baru ini saya menemukan posting blog ini dari monster asp.net yang berbicara tentang masalah penggunaan dengan HttpClient
cara sebagai berikut:
using(var client = new HttpClient())
{
}
Sesuai dengan posting blog, jika kita membuang HttpClient
setelah setiap permintaan itu dapat membuat koneksi TCP tetap terbuka. Ini berpotensi menyebabkan System.Net.Sockets.SocketException
.
Cara yang benar sesuai pos adalah membuat satu contoh HttpClient
karena membantu mengurangi pemborosan soket.
Dari pos:
Jika kami membagikan satu instance HttpClient maka kami dapat mengurangi pemborosan soket dengan menggunakannya kembali:
namespace ConsoleApplication { public class Program { private static HttpClient Client = new HttpClient(); public static void Main(string[] args) { Console.WriteLine("Starting connections"); for(int i = 0; i<10; i++) { var result = Client.GetAsync("http://aspnetmonsters.com").Result; Console.WriteLine(result.StatusCode); } Console.WriteLine("Connections done"); Console.ReadLine(); } } }
Saya selalu membuang HttpClient
objek setelah menggunakannya karena saya merasa ini adalah cara terbaik untuk menggunakannya. Tapi postingan blog ini sekarang membuat saya merasa saya melakukan kesalahan selama ini.
Haruskah kita membuat instance tunggal baru HttpClient
untuk semua permintaan? Apakah ada kesulitan menggunakan instance statis?
sumber
Close()
atau memulai yang baruGet()
. Jika Anda hanya membuang klien setelah selesai, tidak akan ada yang menangani jabat tangan penutup dan port Anda semua akan memiliki status TIME_WAIT, karena itu.Jawaban:
Sepertinya posting blog yang menarik. Namun, sebelum membuat keputusan, pertama-tama saya akan menjalankan tes yang sama dengan yang dilakukan penulis blog, tetapi dengan kode Anda sendiri. Saya juga akan mencoba dan mencari tahu lebih banyak tentang HttpClient dan perilakunya.
Pos ini menyatakan:
Jadi apa yang mungkin terjadi ketika HttpClient dibagikan adalah bahwa koneksi sedang digunakan kembali, yang baik-baik saja jika Anda tidak memerlukan koneksi terus-menerus. Satu-satunya cara Anda akan tahu pasti apakah ini penting atau tidak untuk situasi Anda adalah dengan menjalankan tes kinerja Anda sendiri.
Jika Anda menggali, Anda akan menemukan beberapa sumber daya lain yang mengatasi masalah ini (termasuk artikel Praktik Terbaik Microsoft), jadi mungkin ide yang baik untuk diterapkan pula (dengan beberapa tindakan pencegahan).
Referensi
Anda Menggunakan Httpclient Salah dan
Mengacaukan Perangkat Lunak Anda Singleton HttpClient? Waspadalah terhadap perilaku serius ini dan cara memperbaikinya
Pola dan Praktik Microsoft - Optimalisasi Kinerja: Instansiasi yang Tidak Benar
Satu contoh HttpClient yang dapat digunakan kembali pada Tinjauan Kode
Singleton HttpClient tidak menghargai perubahan DNS (CoreFX)
Saran umum untuk menggunakan HttpClient
sumber
Saya terlambat ke pesta, tapi inilah perjalanan belajar saya tentang topik rumit ini.
1. Di mana kami dapat menemukan advokat resmi untuk menggunakan kembali HttpClient?
Maksud saya, jika menggunakan kembali HttpClient dimaksudkan dan melakukannya adalah penting , advokat seperti itu lebih baik didokumentasikan dalam dokumentasi API-nya sendiri, daripada disembunyikan di banyak "Topik Lanjut", "Pola kinerja (anti)" atau posting blog lainnya di luar sana . Kalau tidak, bagaimana mungkin seorang pelajar baru mengetahuinya sebelum terlambat?
Sampai sekarang (Mei 2018), hasil pencarian pertama ketika googling "c # httpclient" menunjuk ke halaman referensi API ini di MSDN , yang tidak menyebutkan maksud itu sama sekali. Nah, pelajaran 1 di sini untuk pemula adalah, selalu klik tautan "Versi Lain" tepat setelah judul halaman bantuan MSDN, Anda mungkin akan menemukan tautan ke "versi saat ini" di sana. Dalam kasus HttpClient ini, ini akan membawa Anda ke dokumen terbaru di sini yang berisi uraian niat tersebut .
Saya curiga banyak pengembang yang baru mengenal topik ini juga tidak menemukan halaman dokumentasi yang benar, itu sebabnya pengetahuan ini tidak tersebar luas, dan orang-orang terkejut ketika mereka menemukannya nanti , mungkin dengan cara yang sulit .
2. Konsepsi (salah?) Dari
using
IDisposable
Satu ini sedikit di luar topik tapi masih layak menunjuk bahwa, itu bukan kebetulan melihat orang-orang pada mereka posting blog tersebut menyalahkan bagaimana
HttpClient
'sIDisposable
antarmuka membuat mereka cenderung menggunakanusing (var client = new HttpClient()) {...}
pola dan kemudian menyebabkan masalah.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, tidak masalah untuk membocorkan hanya satu koneksi yang terbuka setelah seluruh program Anda berakhir, dan poster blog masih melihat peningkatan kinerja setelah perbaikan; tapi tetap saja, secara konseptual salah untuk menyalahkan IDisposable dan memilih untuk TIDAK membuangnya.3. Apakah kita harus meletakkan HttpClient ke properti statis, atau bahkan menjadikannya sebagai singleton?
Berdasarkan pemahaman dari bagian sebelumnya, saya pikir jawabannya di sini menjadi jelas: "belum tentu". Ini benar-benar tergantung pada bagaimana Anda mengatur kode Anda, selama Anda menggunakan kembali HttpClient AND (idealnya) buang pada akhirnya.
Meriah, bahkan contoh di bagian Keterangan dari dokumen resmi saat ini tidak benar. Ini mendefinisikan kelas "GoodController", yang mengandung properti HttpClient statis yang tidak akan dibuang; yang tidak mematuhi apa contoh lain di bagian Contoh menekankan: "perlu memanggil buang ... sehingga aplikasi tidak membocorkan sumber daya".
Dan terakhir, singleton bukan tanpa tantangannya sendiri.
- Dikutip dari ceramah yang menginspirasi ini, "Negara Global dan Lajang"
PS: Koneksi Sql
Yang ini tidak relevan dengan T&J saat ini, tetapi ini mungkin baik untuk diketahui. Pola penggunaan SqlConnection berbeda. Anda TIDAK perlu menggunakan kembali SqlConnection , karena ia akan menangani kelompok koneksi dengan lebih baik.
Perbedaan tersebut disebabkan oleh pendekatan implementasi mereka. Setiap instance HttpClient menggunakan kumpulan koneksi sendiri (dikutip dari sini ); tetapi SqlConnection sendiri dikelola oleh pool koneksi sentral, menurut ini .
Dan Anda masih perlu membuang SqlConnection, sama seperti yang seharusnya Anda lakukan untuk HttpClient.
sumber
Saya melakukan beberapa tes melihat peningkatan kinerja dengan statis
HttpClient
. Saya menggunakan kode di bawah ini untuk pengujian saya:Untuk pengujian:
Saya menemukan peningkatan kinerja antara 40% hingga 60% menggunakan statis
HttpClient
daripada membuangnya untukHttpClient
permintaan. Saya telah memasukkan rincian hasil tes kinerja di posting blog di sini .sumber
Untuk menutup koneksi TCP dengan benar , kita perlu menyelesaikan urutan paket FIN - FIN + ACK - ACK (seperti SYN - SYN + ACK - ACK, saat membuka koneksi TCP ). Jika kami hanya memanggil metode .Close () (biasanya terjadi ketika HttpClient sedang membuang), dan kami tidak menunggu sisi jarak jauh untuk mengonfirmasi permintaan tutup kami (dengan FIN + ACK), kami berakhir dengan status TIME_WAIT pada port TCP lokal, karena kami membuang pendengar kami (HttpClient) dan kami tidak pernah mendapat kesempatan untuk mengatur ulang status port ke keadaan tertutup yang tepat, setelah rekan jauh mengirimkan paket FIN + ACK.
Cara yang tepat untuk menutup koneksi TCP adalah dengan memanggil metode .Close () dan menunggu acara tutup dari sisi lain (FIN + ACK) untuk tiba di pihak kami. Hanya dengan begitu kita dapat mengirim ACK terakhir kita dan membuang HttpClient.
Hanya untuk menambahkan, masuk akal untuk menjaga koneksi TCP tetap terbuka, jika Anda melakukan permintaan HTTP, karena header HTTP "Connection: Keep-Alive". Lebih jauh lagi, Anda mungkin meminta rekan jauh untuk menutup koneksi untuk Anda, sebagai gantinya, dengan mengatur header HTTP "Connection: Close". Dengan begitu, porta lokal Anda akan selalu ditutup dengan benar, alih-alih dalam keadaan TIME_WAIT.
sumber
Berikut adalah klien API dasar yang menggunakan HttpClient dan HttpClientHandler secara efisien. Saat Anda membuat HttpClient baru untuk membuat permintaan, ada banyak overhead. JANGAN buat kembali HttpClient untuk setiap permintaan. Gunakan kembali HttpClient sebanyak mungkin ...
Pemakaian:
sumber
Tidak ada satu cara untuk menggunakan kelas HttpClient. Kuncinya adalah merancang aplikasi Anda dengan cara yang masuk akal untuk lingkungan dan batasannya.
HTTP adalah protokol yang bagus untuk digunakan ketika Anda perlu mengekspos API publik. Itu juga dapat digunakan secara efektif untuk layanan internal latensi rendah ringan - meskipun pola antrian pesan RPC sering merupakan pilihan yang lebih baik untuk layanan internal.
Ada banyak kerumitan dalam melakukan HTTP dengan baik.
Pertimbangkan yang berikut ini:
Namun yang terpenting, uji, ukur dan konfirmasi. Jika tidak berperilaku seperti yang dirancang, maka kami dapat menjawab pertanyaan spesifik tentang bagaimana mencapai hasil yang Anda harapkan.
sumber
HttpClient
mengimplementasikanIDisposable
. Oleh karena itu tidak masuk akal untuk mengharapkannya menjadi objek berumur pendek yang tahu cara membersihkan setelah itu sendiri, cocok untuk membungkus dalamusing
pernyataan setiap kali Anda membutuhkannya. Sayangnya, itu bukan cara kerjanya yang sebenarnya. Posting blog yang tertaut OP jelas menunjukkan bahwa ada sumber daya (khususnya, koneksi soket TCP) yang hidup lama setelahusing
pernyataan itu keluar dari ruang lingkup danHttpClient
objek mungkin telah dibuang.