Apa yang seharusnya menjadi masa HttpClient
hidup klien WebAPI?
Apakah lebih baik memiliki satu contoh HttpClient
untuk beberapa panggilan?
Berapa overhead untuk membuat dan membuang HttpClient
permintaan, seperti dalam contoh di bawah ini (diambil dari http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from- a-net-client ):
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// New code:
HttpResponseMessage response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync<Product>();
Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Price, product.Category);
}
}
c#
asp.net
web-services
asp.net-web-api
dotnet-httpclient
Bruno Pessanha
sumber
sumber
Stopwatch
kelas untuk membandingkannya. Perkiraan saya akan lebih masuk akal untuk memiliki satuHttpClient
, dengan asumsi semua contoh tersebut digunakan dalam konteks yang sama.Jawaban:
HttpClient
telah dirancang untuk digunakan kembali untuk beberapa panggilan . Bahkan di banyak utas. TheHttpClientHandler
memiliki Kredensial dan Cookie yang dimaksudkan untuk digunakan kembali di seluruh panggilan. MemilikiHttpClient
instance baru memerlukan pengaturan ulang semua hal itu. Selain itu,DefaultRequestHeaders
properti berisi properti yang dimaksudkan untuk beberapa panggilan. Harus mengatur ulang nilai-nilai itu pada setiap permintaan mengalahkan intinya.Manfaat utama lainnya
HttpClient
adalah kemampuan untuk menambahkanHttpMessageHandlers
ke dalam pipa permintaan / respons untuk menerapkan masalah lintas sektoral. Ini bisa untuk pencatatan, audit, pembatasan, penanganan pengalihan, penanganan offline, metrik pengambilan. Segala macam hal berbeda. Jika HttpClient baru dibuat pada setiap permintaan, maka semua penangan pesan ini harus disiapkan pada setiap permintaan dan entah bagaimana keadaan tingkat aplikasi yang dibagi antara permintaan untuk penangan ini juga perlu disediakan.Semakin banyak Anda menggunakan fitur
HttpClient
, semakin Anda akan melihat bahwa menggunakan kembali contoh yang ada masuk akal.Namun, masalah terbesar, menurut saya adalah ketika sebuah
HttpClient
kelas dibuang, ia dibuangHttpClientHandler
, yang kemudian secara paksa menutupTCP/IP
koneksi di kumpulan koneksi yang dikelola olehServicePointManager
. Ini berarti bahwa setiap permintaan dengan yang baruHttpClient
memerlukan membangun kembaliTCP/IP
koneksi baru .Dari pengujian saya, menggunakan HTTP biasa pada LAN, kinerja hit cukup diabaikan. Saya menduga ini karena ada keepalive TCP yang mendasarinya yang menahan koneksi terbuka bahkan ketika
HttpClientHandler
mencoba untuk menutupnya.Atas permintaan yang masuk internet, saya telah melihat cerita yang berbeda. Saya telah melihat hit kinerja 40% karena harus membuka kembali permintaan setiap waktu.
Saya menduga hit pada
HTTPS
koneksi akan lebih buruk.Saran saya adalah untuk membuat instance HttpClient untuk aplikasi Anda seumur hidup untuk setiap API berbeda yang Anda sambungkan.
sumber
which then forcibly closes the TCP/IP connection in the pool of connections that is managed by ServicePointManager
Seberapa yakin Anda tentang pernyataan ini? Itu sulit dipercaya.HttpClient
bagiku tampak seperti unit kerja yang seharusnya sering dipakai.Jika Anda ingin skala aplikasi Anda, perbedaannya BESAR! Tergantung pada bebannya, Anda akan melihat angka kinerja yang sangat berbeda. Seperti yang disebutkan Darrel Miller, HttpClient dirancang untuk digunakan kembali di seluruh permintaan. Ini dikonfirmasi oleh orang-orang di tim BCL yang menulisnya.
Sebuah proyek baru-baru ini yang saya miliki adalah untuk membantu pengecer komputer online yang sangat besar dan terkenal untuk meningkatkan traffic Black Friday / holiday untuk beberapa sistem baru. Kami mengalami beberapa masalah kinerja seputar penggunaan HttpClient. Sejak diterapkan
IDisposable
, para devs melakukan apa yang biasanya Anda lakukan dengan membuat sebuah instance dan menempatkannya di dalam sebuahusing()
pernyataan. Setelah kami mulai memuat pengujian, aplikasi membuat server bertekuk lutut - ya, server bukan hanya aplikasi. Alasannya adalah bahwa setiap instance HttpClient membuka port pada server. Karena finalisasi GC yang non-deterministik dan fakta bahwa Anda bekerja dengan sumber daya komputer yang menjangkau beberapa lapisan OSI , menutup port jaringan dapat memakan waktu cukup lama. Bahkan OS Windows sendiridapat memakan waktu hingga 20 detik untuk menutup port (per Microsoft). Kami membuka port lebih cepat daripada yang bisa ditutup - kelelahan port server yang memalu CPU hingga 100%. Perbaikan saya adalah mengubah HttpClient menjadi instance statis yang menyelesaikan masalah. Ya, ini adalah sumber daya sekali pakai, tetapi setiap overhead jauh lebih besar daripada perbedaan dalam kinerja. Saya mendorong Anda untuk melakukan beberapa pengujian beban untuk melihat bagaimana aplikasi Anda berperilaku.Anda juga dapat memeriksa halaman Panduan WebAPI untuk dokumentasi dan contoh di https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Berikan perhatian khusus pada info ini:
Jika Anda perlu menggunakan statis
HttpClient
dengan tajuk, alamat dasar, dll. Yang perlu Anda lakukan adalah membuatHttpRequestMessage
secara manual dan mengatur nilai-nilai tersebut diHttpRequestMessage
. Lalu, gunakanHttpClient:SendAsync(HttpRequestMessage requestMessage, ...)
PEMBARUAN untuk .NET Core : Anda harus menggunakan
IHttpClientFactory
Injeksi Ketergantungan via untuk membuatHttpClient
instance. Ini akan mengatur masa pakai untuk Anda dan Anda tidak perlu membuangnya secara eksplisit. Lihat Membuat permintaan HTTP menggunakan IHttpClientFactory di ASP.NET Coresumber
Sebagai jawaban lain menyatakan,
HttpClient
dimaksudkan untuk digunakan kembali. Namun, menggunakan kembali satuHttpClient
instance di aplikasi multi-threaded berarti Anda tidak dapat mengubah nilai properti stateful, sepertiBaseAddress
danDefaultRequestHeaders
(jadi Anda hanya dapat menggunakannya jika mereka konstan di aplikasi Anda).Salah satu pendekatan untuk mengatasi batasan ini adalah membungkus
HttpClient
dengan kelas yang menduplikasi semuaHttpClient
metode yang Anda butuhkan (GetAsync
,PostAsync
dll) dan mendelegasikannya ke singletonHttpClient
. Namun itu cukup membosankan (Anda perlu membungkus metode ekstensi juga), dan untungnya ada cara lain - terus membuatHttpClient
contoh baru , tetapi gunakan kembali yang mendasarinyaHttpClientHandler
. Pastikan Anda tidak membuang penangannya:sumber
SendAsync
jauh lebih tidak nyaman daripada metode khusus sepertiPutAsync
,PostAsJsonAsync
dll.Terkait dengan situs web bervolume tinggi tetapi tidak langsung ke HttpClient. Kami memiliki cuplikan kode di bawah ini di semua layanan kami.
Dari https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Net.ServicePoint.ConnectionLeaseTimeout);k(TargetFrameworkMoniker-.Version%3Dv4.5.2); k (DevLang-csharp) & rd = true
"Anda bisa menggunakan properti ini untuk memastikan bahwa koneksi aktif objek ServicePoint tidak tetap terbuka tanpa batas waktu. Properti ini ditujukan untuk skenario di mana koneksi harus dihapus dan dibangun kembali secara berkala, seperti skenario penyeimbangan beban.
Secara default, ketika KeepAlive berlaku untuk permintaan, properti MaxIdleTime menetapkan batas waktu untuk menutup koneksi ServicePoint karena tidak aktif. Jika ServicePoint memiliki koneksi aktif, MaxIdleTime tidak berpengaruh dan koneksi tetap terbuka tanpa batas.
Ketika properti ConnectionLeaseTimeout diatur ke nilai selain -1, dan setelah waktu yang ditentukan berlalu, koneksi ServicePoint aktif ditutup setelah melayani permintaan dengan mengatur KeepAlive ke false dalam permintaan itu. Mengatur nilai ini memengaruhi semua koneksi yang dikelola oleh objek ServicePoint. "
Ketika Anda memiliki layanan di belakang CDN atau titik akhir lain yang Anda ingin gagal, maka pengaturan ini membantu penelepon mengikuti Anda ke tujuan baru Anda. Dalam contoh ini 60 detik setelah kegagalan, semua penelepon harus menghubungkan kembali ke titik akhir yang baru. Itu mengharuskan Anda untuk mengetahui layanan dependen Anda (layanan-layanan yang ANDA panggil) dan titik akhir mereka.
sumber
Anda mungkin juga ingin merujuk ke posting blog ini oleh Simon Timms: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
sumber
Satu hal yang perlu diperhatikan, bahwa tidak ada catatan blog "jangan gunakan menggunakan" adalah bahwa itu bukan hanya BaseAddress dan DefaultHeader yang perlu Anda pertimbangkan. Setelah Anda membuat HttpClient statis, ada kondisi internal yang akan dibawa melintasi permintaan. Contoh: Anda mengautentikasi ke pihak ke-3 dengan HttpClient untuk mendapatkan token FedAuth (abaikan mengapa tidak menggunakan OAuth / OWIN / dll), bahwa pesan Respons memiliki header Set-Cookie untuk FedAuth, ini ditambahkan ke status HttpClient Anda. Pengguna selanjutnya yang masuk ke API Anda akan mengirimkan cookie FedAuth orang terakhir kecuali Anda mengelola cookie ini pada setiap permintaan.
sumber
Sebagai masalah pertama, walaupun kelas ini dapat dibuang, menggunakannya dengan
using
pernyataan bukanlah pilihan terbaik karena bahkan ketika Anda membuangnyaHttpClient
objek, soket yang mendasarinya tidak segera dilepaskan dan dapat menyebabkan masalah serius yang bernama 'sockets exhaustion.Tapi ada masalah kedua dengan
HttpClient
yang bisa Anda miliki ketika Anda menggunakannya sebagai objek tunggal atau statis. Dalam hal ini, singleton atau statisHttpClient
tidak menghormatiDNS
perubahan.dalam .net core Anda dapat melakukan hal yang sama dengan HttpClientFactory seperti ini:
Konfigurasikan Layanan
dokumentasi dan contoh di sini
sumber