Bagikan melalui


Gunakan IHttpClientFactory untuk menerapkan permintaan yang tangguh

Tip

Konten ini adalah kutipan dari eBook, .NET Microservices Architecture for Containerized .NET Applications, tersedia di .NET Docs atau sebagai PDF yang dapat diunduh gratis dan dapat dibaca secara offline.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

IHttpClientFactory adalah kontrak yang diimplementasikan oleh DefaultHttpClientFactory, pabrik dogmatis, tersedia sejak .NET Core 2.1, untuk membuat instans HttpClient yang akan digunakan dalam aplikasi Anda.

Masalah dengan kelas HttpClient asli yang tersedia di .NET

Kelas HttpClient asli dan dikenal dapat dengan mudah digunakan, tetapi dalam beberapa kasus, kelas tersebut tidak digunakan dengan benar oleh banyak pengembang.

Meskipun kelas ini mengimplementasikan IDisposable, mendeklarasikan dan membuat instansnya dalam using pernyataan tidak disukai karena ketika objek HttpClient dibuang, soket yang mendasarinya tidak segera dirilis, ini dapat menyebabkan masalah soket hampir habis. Untuk informasi selengkapnya tentang masalah ini, lihat postingan blog Anda salah menggunakan HttpClient dan membuat perangkat lunak Anda tidak stabil.

Oleh karena itu, HttpClient dimaksudkan untuk dipakai sekali, kemudian digunakan kembali sepanjang masa pakai aplikasi. Membuat instans kelas HttpClient untuk setiap permintaan akan menghabiskan jumlah soket yang tersedia di bawah beban berat. Masalah tersebut akan mengakibatkan kesalahan SocketException. Kemungkinan pendekatan untuk memecahkan masalah itu didasarkan pada pembuatan objek HttpClient sebagai tunggal atau statis, seperti yang dijelaskan dalam artikel Microsoft ini tentang penggunaan HttpClient. Ini bisa menjadi solusi yang baik untuk aplikasi konsol berumur pendek atau serupa, yang berjalan beberapa kali sehari.

Masalah lain yang dihadapi pengembang adalah saat menggunakan instans HttpClient bersama dalam proses yang berjalan lama. Dalam situasi di mana HttpClient dibuat sebagai singleton atau objek statis, httpClient gagal menangani perubahan DNS seperti yang dijelaskan dalam masalah repositori GitHub dotnet/runtime ini.

Namun, masalahnya tidak benar-benar dengan HttpClient per se, tetapi dengan konstruktor default untuk HttpClient, karena menciptakan instans konkret baru, HttpMessageHandleryang merupakan salah satu yang memiliki masalah soket hampir habis dan perubahan DNS yang disebutkan di atas.

Untuk mengatasi masalah yang disebutkan di atas dan untuk membuat HttpClient instans dapat dikelola, .NET Core 2.1 memperkenalkan dua pendekatan, salah satunya adalah IHttpClientFactory. Ini adalah antarmuka yang digunakan untuk mengonfigurasi dan membuat HttpClient instans di aplikasi melalui Dependency Injection (DI). Ini juga menyediakan ekstensi untuk middleware berbasis Polly untuk memanfaatkan pendelegasian penanganan di HttpClient.

Alternatifnya adalah menggunakan SocketsHttpHandler dengan dikonfigurasi PooledConnectionLifetime. Pendekatan ini diterapkan pada instans berumur panjang, static atau singleton HttpClient . Untuk mempelajari selengkapnya tentang strategi yang berbeda, lihat Panduan HttpClient untuk .NET.

Polly adalah pustaka penanganan kesalahan sementara yang membantu pengembang menambahkan ketahanan ke aplikasi mereka, dengan menggunakan beberapa kebijakan yang telah ditentukan sebelumnya dengan cara yang lancar dan aman.

Manfaat menggunakan IHttpClientFactory

Implementasi saat ini dari IHttpClientFactory, yang juga mengimplementasikan IHttpMessageHandlerFactory, memberikan manfaat berikut:

  • Menyediakan lokasi pusat untuk penamaan dan konfigurasi objekHttpClient logis. Misalnya, Anda dapat mengonfigurasi klien (Agen Layanan) yang telah dikonfigurasi sebelumnya untuk mengakses layanan mikro tertentu.
  • Mengodifikasi konsep middleware keluar melalui pendelegasian penanganan di HttpClient dan mengimplementasikan middleware berbasis Polly untuk memanfaatkan kebijakan Polly untuk ketahanan.
  • HttpClient sudah memiliki konsep mendelegasikan penanganan yang dapat ditautkan bersama untuk permintaan HTTP keluar. Anda dapat mendaftarkan klien HTTP ke pabrik dan Anda dapat menggunakan pengendali Polly untuk menggunakan kebijakan Polly untuk Coba Lagi, Pemutus Sirkuit, dan sebagainya.
  • Kelola masa pakai HttpMessageHandler untuk menghindari masalah/masalah yang disebutkan yang dapat terjadi saat mengelola masa pakai HttpClient sendiri.

Tip

Instans HttpClient yang disuntikkan oleh DI dapat dibuang dengan aman, karena yang terkait HttpMessageHandler dikelola oleh pabrik. Instans yang disuntikkan HttpClient bersifat Sementara dari perspektif DI, sementara HttpMessageHandler instans dapat dianggap sebagai Cakupan. HttpMessageHandler instans memiliki cakupan DI mereka sendiri, terpisah dari cakupan aplikasi (misalnya, ASP.NET cakupan permintaan masuk). Untuk informasi selengkapnya, lihat Menggunakan HttpClientFactory di .NET.

Catatan

Implementasi IHttpClientFactory (DefaultHttpClientFactory) terkait erat dengan implementasi DI dalam paket NuGet Microsoft.Extensions.DependencyInjection. Jika Anda perlu menggunakan HttpClient tanpa DI atau dengan implementasi DI lainnya, pertimbangkan untuk menggunakan static atau singleton HttpClient dengan PooledConnectionLifetime penyiapan. Untuk informasi selengkapnya, lihat Panduan HttpClient untuk .NET.

Beberapa cara untuk menggunakan IHttpClientFactory

Ada beberapa cara yang dapat Anda gunakan IHttpClientFactory dalam aplikasi Anda:

  • Penggunaan dasar
  • Gunakan Klien Bernama
  • Gunakan Klien Berjenis
  • Gunakan Klien yang Dihasilkan

Untuk singkatnya, panduan ini menunjukkan cara yang paling terstruktur untuk menggunakan IHttpClientFactory, yaitu dengan menggunakan Typed Clients (pola Agen Layanan). Tetapi, semua opsi didokumentasikan dan saat ini tercantum dalam artikel ini yang mencakup IHttpClientFactory penggunaan.

Catatan

Jika aplikasi Anda memerlukan cookie, mungkin lebih baik untuk menghindari penggunaan IHttpClientFactory di aplikasi Anda. Untuk cara alternatif mengelola klien, lihat Panduan untuk menggunakan klien HTTP

Cara menggunakan Klien Berjenis dengan IHttpClientFactory

Jadi, apa itu "Klien Berjenis"? Ini adalah HttpClient yang telah dikonfigurasi sebelumnya untuk beberapa penggunaan tertentu. Konfigurasi ini dapat mencakup nilai-nilai tertentu seperti server dasar, header HTTP atau waktu habis.

Diagram berikut menunjukkan bagaimana Klien Berjenis digunakan dengan IHttpClientFactory:

Diagram showing how typed clients are used with IHttpClientFactory.

Gambar 8-4. Menggunakan IHttpClientFactory dengan kelas Klien Berjenis.

Pada gambar di atas, ClientService (digunakan oleh pengontrol atau kode klien) menggunakan yang HttpClient dibuat oleh IHttpClientFactory yang terdaftar. Pabrik ini menetapkan HttpMessageHandler dari kumpulan ke HttpClient. HttpClient dapat dikonfigurasi dengan kebijakan Polly saat mendaftarkan IHttpClientFactory dalam kontainer DI dengan metode ekstensi AddHttpClient.

Untuk mengonfigurasi struktur di atas, tambahkan IHttpClientFactory di aplikasi Anda dengan memasang peket NuGet Microsoft.Extensions.Http yang menyertakan metode ekstensi AddHttpClient untuk IServiceCollection. Metode ekstensi ini mendaftarkan kelas DefaultHttpClientFactory internal yang akan digunakan sebagai singleton untuk antarmuka IHttpClientFactory. Ini mendefinisikan konfigurasi sementara untuk HttpMessageHandlerBuilder. Penanganan pesan (objek HttpMessageHandler) ini, diambil dari kumpulan, digunakan oleh HttpClient dikembalikan dari pabrik.

Dalam cuplikan berikutnya, Anda dapat melihat bagaimana AddHttpClient() dapat digunakan untuk mendaftarkan Klien Jenis (Agen Layanan) yang perlu menggunakan HttpClient.

// Program.cs
//Add http client services at ConfigureServices(IServiceCollection services)
builder.Services.AddHttpClient<ICatalogService, CatalogService>();
builder.Services.AddHttpClient<IBasketService, BasketService>();
builder.Services.AddHttpClient<IOrderingService, OrderingService>();

Mendaftarkan layanan klien seperti yang ditunjukkan pada cuplikan sebelumnya, membuat DefaultClientFactory membuat standar HttpClient untuk setiap layanan. Klien berjenis terdaftar sebagai sementara dengan kontainer DI. Dalam kode sebelumnya, AddHttpClient() mendaftarkan CatalogService, BasketService, OrderingService sebagai layanan sementara sehingga mereka dapat disuntikkan dan digunakan langsung tanpa perlu registrasi tambahan.

Anda juga dapat menambahkan konfigurasi khusus instans dalam pendaftaran ke, misalnya, mengonfigurasi alamat dasar, dan menambahkan beberapa kebijakan ketahanan, seperti yang ditunjukkan dalam hal berikut:

builder.Services.AddHttpClient<ICatalogService, CatalogService>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["BaseUrl"]);
})
    .AddPolicyHandler(GetRetryPolicy())
    .AddPolicyHandler(GetCircuitBreakerPolicy());

Dalam contoh berikutnya ini, Anda dapat melihat konfigurasi salah satu kebijakan di atas:

static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
        .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}

Anda dapat menemukan detail selengkapnya tentang menggunakan Polly di artikel Berikutnya.

Masa pakai HttpClient

Setiap kali Anda mendapatkan objek HttpClient dari IHttpClientFactory, instans baru akan dikembalikan. Tetapi masing-masing HttpClient menggunakan HttpMessageHandler yang dikumpulkan dan digunakan kembali oleh IHttpClientFactory untuk mengurangi konsumsi sumber daya, selama masa pakai HttpMessageHandler belum kedaluwarsa.

Pengumpulan penanganan diinginkan karena setiap penanganan biasanya mengelola sambungan HTTP yang mendasarinya sendiri; membuat lebih banyak penanganan dibandingkan yang diperlukan dapat mengakibatkan penundaan sambungan. Beberapa penanganan juga menjaga koneksi tetap terbuka tanpa batas, yang dapat mencegah penanganan bereaksi terhadap perubahan DNS.

Objek HttpMessageHandler di kumpulan memiliki masa pakai yang merupakan lamanya waktu instans HttpMessageHandler di kumpulan dapat digunakan kembali. Nilai defaultnya adalah dua menit, tetapi dapat ditimpa per Klien Berjenis. Untuk mengambilalih, panggil SetHandlerLifetime()IHttpClientBuilder yang dikembalikan saat membuat klien, seperti yang ditunjukkan dalam kode berikut:

//Set 5 min as the lifetime for the HttpMessageHandler objects in the pool used for the Catalog Typed Client
builder.Services.AddHttpClient<ICatalogService, CatalogService>()
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

Setiap Klien Berjenis dapat memiliki nilai masa pakai penanganan yang dikonfigurasi sendiri. Atur masa pakai ke InfiniteTimeSpan untuk menonaktifkan kedaluwarsa penanganan.

Terapkan kelas Klien Berjenis Anda yang menggunakan HttpClient yang disuntikkan dan dikonfigurasi

Sebagai langkah sebelumnya, Anda harsu menentukan kelas Klien Berjenis, seperti kelas dalam kode sampel, seperti 'BasketService', 'CatalogService', 'OrderingService', dll. – Klien Berjenis adalah kelas yang menerima objek HttpClient (disuntikkan melalui konstruktornya) dan menggunakannya untuk memanggil beberapa layanan HTTP jarak jauh. Misalnya:

public class CatalogService : ICatalogService
{
    private readonly HttpClient _httpClient;
    private readonly string _remoteServiceBaseUrl;

    public CatalogService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<Catalog> GetCatalogItems(int page, int take,
                                               int? brand, int? type)
    {
        var uri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl,
                                                 page, take, brand, type);

        var responseString = await _httpClient.GetStringAsync(uri);

        var catalog = JsonConvert.DeserializeObject<Catalog>(responseString);
        return catalog;
    }
}

Klien Jenis (CatalogService dalam contoh) diaktifkan oleh DI (Injeksi Dependensi), yang berarti dapat menerima layanan terdaftar apa pun di konstruktornya, selain HttpClient.

Klien Berjenis secara efektif adalah objek sementara, yang berarti instans baru dibuat setiap kali diperlukan. Ini menerima instans HttpClient baru setiap kali dibangun. Tetapi objek HttpMessageHandler di kumpulan adalah objek yang digunakan kembali oleh beberapa instans HttpClient.

Menggunakan kelas Klien Berjenis

Terakhir, setelah kelas yang diketik diimplementasikan, Anda dapat mendaftarkannya dan dikonfigurasi dengan AddHttpClient(). Setelah itu Anda dapat menggunakannya di mana pun layanan disuntikkan oleh DI, seperti di kode halaman Razor atau pengontrol aplikasi web MVC, yang ditunjukkan dalam kode di bawah ini dari eShopOnContainers:

namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{
    public class CatalogController : Controller
    {
        private ICatalogService _catalogSvc;

        public CatalogController(ICatalogService catalogSvc) =>
                                                           _catalogSvc = catalogSvc;

        public async Task<IActionResult> Index(int? BrandFilterApplied,
                                               int? TypesFilterApplied,
                                               int? page,
                                               [FromQuery]string errorMsg)
        {
            var itemsPage = 10;
            var catalog = await _catalogSvc.GetCatalogItems(page ?? 0,
                                                            itemsPage,
                                                            BrandFilterApplied,
                                                            TypesFilterApplied);
            //… Additional code
        }

        }
}

Hingga titik ini, cuplikan kode di atas hanya menunjukkan contoh melakukan permintaan HTTP reguler. Tetapi 'sihir' hadir di bagian berikut di mana ia menunjukkan bagaimana semua permintaan HTTP yang dibuat oleh HttpClient dapat memiliki kebijakan tangguh seperti mencoba kembali dengan backoff eksponensial, pemutus sirkuit, fitur keamanan menggunakan token autentikasi, atau bahkan fitur kustom lainnya. Dan semua ini dapat dilakukan hanya dengan menambahkan kebijakan dan mendelegasikan penanganan ke Klien Berjenis terdaftar Anda.

Sumber daya tambahan