Bagikan melalui


Membangun aplikasi HTTP tangguh: Pola pengembangan kunci

Membangun aplikasi HTTP yang kuat yang dapat pulih dari kesalahan sementara adalah persyaratan umum. Artikel ini mengasumsikan bahwa Anda telah membaca Pengantar pengembangan aplikasi tangguh, karena artikel ini memperluas konsep inti yang disampaikan. Untuk membantu membangun aplikasi HTTP yang tangguh, paket NuGet Microsoft.Extensions.Http.Resilience menyediakan mekanisme ketahanan HttpClientkhusus untuk . Paket NuGet ini bergantung pada Microsoft.Extensions.Resilience pustaka dan Polly, yang merupakan proyek sumber terbuka yang populer. Untuk informasi selengkapnya, lihat Polly.

Memulai

Untuk menggunakan pola ketahanan di aplikasi HTTP, instal paket NuGet Microsoft.Extensions.Http.Resilience .

dotnet add package Microsoft.Extensions.Http.Resilience --version 8.0.0

Untuk informasi selengkapnya, lihat menambahkan paket dotnet atau Mengelola dependensi paket dalam aplikasi .NET.

Menambahkan ketahanan ke klien HTTP

Untuk menambahkan ketahanan ke HttpClient, Anda merantai panggilan pada IHttpClientBuilder jenis yang dikembalikan dari memanggil salah satu metode yang tersedia AddHttpClient . Untuk informasi selengkapnya, lihat IHttpClientFactory dengan .NET.

Ada beberapa ekstensi yang ber sentris ketahanan yang tersedia. Beberapa standar, sehingga menggunakan berbagai praktik terbaik industri, dan yang lain lebih dapat disesuaikan. Saat menambahkan ketahanan, Anda hanya boleh menambahkan satu handler ketahanan dan menghindari penangan tumpukan. Jika Anda perlu menambahkan beberapa handler ketahanan, Anda harus mempertimbangkan untuk menggunakan AddResilienceHandler metode ekstensi, yang memungkinkan Anda menyesuaikan strategi ketahanan.

Penting

Semua contoh dalam artikel ini mengandalkan AddHttpClient API, dari pustaka Microsoft.Extensions.Http , yang mengembalikan instans IHttpClientBuilder . IHttpClientBuilder Instans digunakan untuk mengonfigurasi HttpClient dan menambahkan handler ketahanan.

Menambahkan handler ketahanan standar

Handler ketahanan standar menggunakan beberapa strategi ketahanan yang ditumpuk satu sama lain, dengan opsi default untuk mengirim permintaan dan menangani kesalahan sementara. Handler ketahanan standar ditambahkan dengan memanggil AddStandardResilienceHandler metode ekstensi pada IHttpClientBuilder instans.

var services = new ServiceCollection();

var httpClientBuilder = services.AddHttpClient<ExampleClient>(
    configureClient: static client =>
    {
        client.BaseAddress = new("https://jsonplaceholder.typicode.com");
    });

Kode sebelumnya:

  • Membuat instans ServiceCollection .
  • HttpClient Menambahkan untuk ExampleClient jenis ke kontainer layanan.
  • HttpClient Mengonfigurasi untuk digunakan "https://jsonplaceholder.typicode.com" sebagai alamat dasar.
  • httpClientBuilder Membuat yang digunakan di seluruh contoh lain dalam artikel ini.

Contoh yang lebih nyata akan mengandalkan hosting, seperti yang dijelaskan dalam artikel .NET Generic Host . Menggunakan paket NuGet Microsoft.Extensions.Hosting, pertimbangkan contoh yang diperbarui berikut:

using Http.Resilience.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

IHttpClientBuilder httpClientBuilder = builder.Services.AddHttpClient<ExampleClient>(
    configureClient: static client =>
    {
        client.BaseAddress = new("https://jsonplaceholder.typicode.com");
    });

Kode sebelumnya mirip dengan pendekatan pembuatan manual ServiceCollection , tetapi sebaliknya bergantung pada Host.CreateApplicationBuilder() untuk membangun host yang mengekspos layanan.

didefinisikan ExampleClient sebagai berikut:

using System.Net.Http.Json;

namespace Http.Resilience.Example;

/// <summary>
/// An example client service, that relies on the <see cref="HttpClient"/> instance.
/// </summary>
/// <param name="client">The given <see cref="HttpClient"/> instance.</param>
internal sealed class ExampleClient(HttpClient client)
{
    /// <summary>
    /// Returns an <see cref="IAsyncEnumerable{T}"/> of <see cref="Comment"/>s.
    /// </summary>
    public IAsyncEnumerable<Comment?> GetCommentsAsync()
    {
        return client.GetFromJsonAsAsyncEnumerable<Comment>("/comments");
    }
}

Kode sebelumnya:

  • ExampleClient Menentukan jenis yang memiliki konstruktor yang menerima HttpClient.
  • GetCommentsAsync Mengekspos metode yang mengirim permintaan GET ke /comments titik akhir dan mengembalikan respons.

Jenis didefinisikan Comment sebagai berikut:

namespace Http.Resilience.Example;

public record class Comment(
    int PostId, int Id, string Name, string Email, string Body);

Mengingat bahwa Anda telah membuat IHttpClientBuilder (httpClientBuilder), dan Sekarang Anda memahami ExampleClient implementasi dan model yang Comment sesuai, pertimbangkan contoh berikut:

httpClientBuilder.AddStandardResilienceHandler();

Kode sebelumnya menambahkan handler ketahanan standar ke HttpClient. Seperti kebanyakan API ketahanan, ada kelebihan beban yang memungkinkan Anda menyesuaikan opsi default dan menerapkan strategi ketahanan.

Default handler ketahanan standar

Konfigurasi default merantai lima strategi ketahanan dalam urutan berikut (dari terluar ke terdahulu):

Pesanan Strategi Deskripsi Default
1 Pembatas tarif Alur pembatas tarif membatasi jumlah maksimum permintaan bersamaan yang dikirim ke dependensi. Antrean: 0
Biar: 1_000
2 Total batas waktu Total alur batas waktu permintaan menerapkan batas waktu keseluruhan untuk eksekusi, memastikan bahwa permintaan, termasuk upaya coba lagi, tidak melebihi batas yang dikonfigurasi. Total batas waktu: 30 detik
3 Coba lagi Alur coba lagi mencoba kembali permintaan jika dependensi lambat atau mengembalikan kesalahan sementara. Percobaan ulang maks: 3
Backoff: Exponential
Gunakan jitter: true
Penundaan:2s
4 Pemutus sirkuit Pemutus sirkuit memblokir eksekusi jika terlalu banyak kegagalan langsung atau batas waktu terdeteksi. Rasio kegagalan: 10%
Throughput min: 100
Durasi pengambilan sampel: 30 detik
Durasi istirahat: 5s
5 Batas waktu percobaan Alur batas waktu percobaan membatasi setiap durasi upaya permintaan dan melempar jika terlampaui. Batas waktu percobaan: 10 detik

Coba lagi dan pemutus sirkuit

Strategi pemutus coba lagi dan sirkuit menangani serangkaian kode status HTTP dan pengecualian tertentu. Pertimbangkan kode status HTTP berikut:

  • HTTP 500 ke atas (Kesalahan server)
  • HTTP 408 (Batas waktu permintaan)
  • HTTP 429 (Terlalu banyak permintaan)

Selain itu, strategi ini menangani pengecualian berikut:

  • HttpRequestException
  • TimeoutRejectedException

Menonaktifkan percobaan ulang untuk daftar metode HTTP tertentu

Secara default, handler ketahanan standar dikonfigurasi untuk melakukan percobaan ulang untuk semua metode HTTP. Untuk beberapa aplikasi, perilaku tersebut bisa tidak diinginkan atau bahkan berbahaya. Misalnya, jika permintaan POST menyisipkan rekaman baru ke database, maka melakukan percobaan ulang untuk permintaan tersebut dapat menyebabkan duplikasi data. Jika Anda perlu menonaktifkan percobaan ulang untuk daftar metode HTTP tertentu, Anda dapat menggunakan metode DisableFor(HttpRetryStrategyOptions, HttpMethod[]):

httpClientBuilder.AddStandardResilienceHandler(options =>
{
    options.Retry.DisableFor(HttpMethod.Post, HttpMethod.Delete);
});

Atau, Anda dapat menggunakan metode DisableForUnsafeHttpMethods(HttpRetryStrategyOptions), yang menonaktifkan percobaan ulang untuk permintaan POST, PATCH, PUT, DELETE, dan CONNECT. Menurut RFC, metode ini dianggap tidak aman; yang berarti semantik dari metode tersebut tidak bersifat hanya-baca:

httpClientBuilder.AddStandardResilienceHandler(options =>
{
    options.Retry.DisableForUnsafeHttpMethods();
});

Menambahkan handler hedging standar

Handler hedging standar membungkus eksekusi permintaan dengan mekanisme hedging standar. Hedging mencoba kembali permintaan lambat secara paralel.

Untuk menggunakan handler hedging standar, panggil AddStandardHedgingHandler metode ekstensi. Contoh berikut mengonfigurasi ExampleClient untuk menggunakan handler hedging standar.

httpClientBuilder.AddStandardHedgingHandler();

Kode sebelumnya menambahkan handler hedging standar ke HttpClient.

Default handler hedging standar

Hedging standar menggunakan kumpulan pemutus sirkuit untuk memastikan bahwa titik akhir yang tidak sehat tidak di-lindungi. Secara default, pilihan dari kumpulan didasarkan pada otoritas URL (skema + host + port).

Tip

Disarankan agar Anda mengonfigurasi cara strategi dipilih dengan memanggil StandardHedgingHandlerBuilderExtensions.SelectPipelineByAuthority atau StandardHedgingHandlerBuilderExtensions.SelectPipelineBy untuk skenario yang lebih canggih.

Kode sebelumnya menambahkan handler hedging standar ke IHttpClientBuilder. Konfigurasi default merantai lima strategi ketahanan dalam urutan berikut (dari terluar ke terdahulu):

Pesanan Strategi Deskripsi Default
1 Total batas waktu permintaan Total alur batas waktu permintaan menerapkan batas waktu keseluruhan untuk eksekusi, memastikan bahwa permintaan, termasuk upaya hedging, tidak melebihi batas yang dikonfigurasi. Total batas waktu: 30 detik
2 Lindung nilai Strategi hedging menjalankan permintaan terhadap beberapa titik akhir jika dependensi lambat atau mengembalikan kesalahan sementara. Perutean adalah opsi, secara default hanya menguraikan URL yang disediakan oleh aslinya HttpRequestMessage. Upaya min: 1
Upaya maks: 10
Penundaan: 2 detik
3 Pembatas tarif (per titik akhir) Alur pembatas tarif membatasi jumlah maksimum permintaan bersamaan yang dikirim ke dependensi. Antrean: 0
Biar: 1_000
4 Pemutus sirkuit (per titik akhir) Pemutus sirkuit memblokir eksekusi jika terlalu banyak kegagalan langsung atau batas waktu terdeteksi. Rasio kegagalan: 10%
Throughput min: 100
Durasi pengambilan sampel: 30 detik
Durasi istirahat: 5s
5 Batas waktu percobaan (per titik akhir) Alur batas waktu percobaan membatasi setiap durasi upaya permintaan dan melempar jika terlampaui. Waktu habis: 10 detik

Menyesuaikan pemilihan rute handler hedging

Saat menggunakan handler hedging standar, Anda dapat menyesuaikan cara titik akhir permintaan dipilih dengan memanggil berbagai ekstensi pada jenisnya IRoutingStrategyBuilder . Ini dapat berguna untuk skenario seperti pengujian A/B, di mana Anda ingin merutekan persentase permintaan ke titik akhir yang berbeda:

httpClientBuilder.AddStandardHedgingHandler(static (IRoutingStrategyBuilder builder) =>
{
    // Hedging allows sending multiple concurrent requests
    builder.ConfigureOrderedGroups(static options =>
    {
        options.Groups.Add(new UriEndpointGroup()
        {
            Endpoints =
            {
                // Imagine a scenario where 3% of the requests are 
                // sent to the experimental endpoint.
                new() { Uri = new("https://example.net/api/experimental"), Weight = 3 },
                new() { Uri = new("https://example.net/api/stable"), Weight = 97 }
            }
        });
    });
});

Kode sebelumnya:

  • Menambahkan handler hedging ke IHttpClientBuilder.
  • IRoutingStrategyBuilder Mengonfigurasi untuk menggunakan ConfigureOrderedGroups metode untuk mengonfigurasi grup yang diurutkan.
  • EndpointGroup Menambahkan ke orderedGroup yang merutekan 3% permintaan ke https://example.net/api/experimental titik akhir dan 97% permintaan ke https://example.net/api/stable titik akhir.
  • IRoutingStrategyBuilder Mengonfigurasi untuk menggunakan ConfigureWeightedGroups metode untuk mengonfigurasi

Untuk mengonfigurasi grup tertimbang, panggil ConfigureWeightedGroups metode pada jenis .IRoutingStrategyBuilder Contoh berikut mengonfigurasi IRoutingStrategyBuilder untuk menggunakan ConfigureWeightedGroups metode untuk mengonfigurasi grup tertimbang.

httpClientBuilder.AddStandardHedgingHandler(static (IRoutingStrategyBuilder builder) =>
{
    // Hedging allows sending multiple concurrent requests
    builder.ConfigureWeightedGroups(static options =>
    {
        options.SelectionMode = WeightedGroupSelectionMode.EveryAttempt;

        options.Groups.Add(new WeightedUriEndpointGroup()
        {
            Endpoints =
            {
                // Imagine A/B testing
                new() { Uri = new("https://example.net/api/a"), Weight = 33 },
                new() { Uri = new("https://example.net/api/b"), Weight = 33 },
                new() { Uri = new("https://example.net/api/c"), Weight = 33 }
            }
        });
    });
});

Kode sebelumnya:

  • Menambahkan handler hedging ke IHttpClientBuilder.
  • IRoutingStrategyBuilder Mengonfigurasi untuk menggunakan ConfigureWeightedGroups metode untuk mengonfigurasi grup tertimbang.
  • Mengatur ke SelectionModeWeightedGroupSelectionMode.EveryAttempt.
  • WeightedEndpointGroup Menambahkan ke weightedGroup yang merutekan 33% permintaan ke https://example.net/api/a titik akhir, 33% permintaan ke https://example.net/api/b titik akhir, dan 33% permintaan ke https://example.net/api/c titik akhir.

Tip

Jumlah maksimum upaya hedging berkorelasi langsung dengan jumlah grup yang dikonfigurasi. Misalnya, jika Anda memiliki dua grup, jumlah maksimum upaya adalah dua.

Untuk informasi selengkapnya, lihat dokumen Polly: Strategi ketahanan hedging.

Umum untuk mengonfigurasi grup yang diurutkan atau grup tertimbang, tetapi valid untuk mengonfigurasi keduanya. Menggunakan grup yang diurutkan dan tertimbang sangat membantu dalam skenario di mana Anda ingin mengirim persentase permintaan ke titik akhir yang berbeda, seperti halnya pengujian A/B.

Menambahkan handler ketahanan kustom

Untuk memiliki lebih banyak kontrol, Anda dapat menyesuaikan handler ketahanan dengan menggunakan AddResilienceHandler API. Metode ini menerima delegasi yang mengonfigurasi instans ResiliencePipelineBuilder<HttpResponseMessage> yang digunakan untuk membuat strategi ketahanan.

Untuk mengonfigurasi handler ketahanan bernama, panggil AddResilienceHandler metode ekstensi dengan nama handler. Contoh berikut mengonfigurasi handler ketahanan bernama yang disebut "CustomPipeline".

httpClientBuilder.AddResilienceHandler(
    "CustomPipeline",
    static builder =>
{
    // See: https://www.pollydocs.org/strategies/retry.html
    builder.AddRetry(new HttpRetryStrategyOptions
    {
        // Customize and configure the retry logic.
        BackoffType = DelayBackoffType.Exponential,
        MaxRetryAttempts = 5,
        UseJitter = true
    });

    // See: https://www.pollydocs.org/strategies/circuit-breaker.html
    builder.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions
    {
        // Customize and configure the circuit breaker logic.
        SamplingDuration = TimeSpan.FromSeconds(10),
        FailureRatio = 0.2,
        MinimumThroughput = 3,
        ShouldHandle = static args =>
        {
            return ValueTask.FromResult(args is
            {
                Outcome.Result.StatusCode:
                    HttpStatusCode.RequestTimeout or
                        HttpStatusCode.TooManyRequests
            });
        }
    });

    // See: https://www.pollydocs.org/strategies/timeout.html
    builder.AddTimeout(TimeSpan.FromSeconds(5));
});

Kode sebelumnya:

  • Menambahkan handler ketahanan dengan nama "CustomPipeline" sebagai ke pipelineName kontainer layanan.
  • Menambahkan strategi coba lagi dengan backoff eksponensial, lima percobaan ulang, dan preferensi jitter ke pembangun ketahanan.
  • Menambahkan strategi pemutus sirkuit dengan durasi pengambilan sampel 10 detik, rasio kegagalan 0,2 (20%), throughput minimum tiga, dan predikat yang menangani RequestTimeout dan TooManyRequests kode status HTTP ke pembangun ketahanan.
  • Menambahkan strategi batas waktu dengan batas waktu lima detik ke pembangun ketahanan.

Ada banyak opsi yang tersedia untuk setiap strategi ketahanan. Untuk informasi selengkapnya, lihat dokumen Polly: Strategi. Untuk informasi selengkapnya tentang mengonfigurasi ShouldHandle delegasi, lihat dokumen Polly: Penanganan kesalahan dalam strategi reaktif.

Muat ulang dinamis

Polly mendukung pengisian ulang dinamis strategi ketahanan yang dikonfigurasi. Ini berarti Anda dapat mengubah konfigurasi strategi ketahanan pada waktu proses. Untuk mengaktifkan muatan ulang dinamis, gunakan kelebihan beban yang sesuai AddResilienceHandler yang mengekspos ResilienceHandlerContext. Mengingat konteksnya, panggilan EnableReloads opsi strategi ketahanan yang sesuai:

httpClientBuilder.AddResilienceHandler(
    "AdvancedPipeline",
    static (ResiliencePipelineBuilder<HttpResponseMessage> builder,
        ResilienceHandlerContext context) =>
    {
        // Enable reloads whenever the named options change
        context.EnableReloads<HttpRetryStrategyOptions>("RetryOptions");

        // Retrieve the named options
        var retryOptions =
            context.GetOptions<HttpRetryStrategyOptions>("RetryOptions");

        // Add retries using the resolved options
        builder.AddRetry(retryOptions);
    });

Kode sebelumnya:

  • Menambahkan handler ketahanan dengan nama "AdvancedPipeline" sebagai ke pipelineName kontainer layanan.
  • Mengaktifkan muatan "AdvancedPipeline" ulang alur setiap kali opsi bernama RetryStrategyOptions berubah.
  • Mengambil opsi bernama dari IOptionsMonitor<TOptions> layanan.
  • Menambahkan strategi coba lagi dengan opsi yang diambil ke penyusun ketahanan.

Untuk informasi selengkapnya, lihat dokumen Polly: Injeksi dependensi tingkat lanjut.

Contoh ini bergantung pada bagian opsi yang mampu diubah, seperti file appsettings.json . Pertimbangkan file appsettings.json berikut:

{
    "RetryOptions": {
        "Retry": {
            "BackoffType": "Linear",
            "UseJitter": false,
            "MaxRetryAttempts": 7
        }
    }
}

Sekarang bayangkan bahwa opsi ini terikat ke konfigurasi aplikasi, mengikat ke HttpRetryStrategyOptions bagian "RetryOptions" :

var section = builder.Configuration.GetSection("RetryOptions");

builder.Services.Configure<HttpStandardResilienceOptions>(section);

Untuk informasi selengkapnya, lihat Pola opsi di .NET.

Contoh penggunaan

Aplikasi Anda bergantung pada injeksi dependensi untuk menyelesaikan ExampleClient dan yang sesuai HttpClient. Kode membangun IServiceProvider dan menyelesaikan ExampleClient darinya.

IHost host = builder.Build();

ExampleClient client = host.Services.GetRequiredService<ExampleClient>();

await foreach (Comment? comment in client.GetCommentsAsync())
{
    Console.WriteLine(comment);
}

Kode sebelumnya:

Bayangkan situasi di mana jaringan tidak berfungsi atau server menjadi tidak responsif. Diagram berikut menunjukkan bagaimana strategi ketahanan akan menangani situasi, mengingat ExampleClient dan GetCommentsAsync metode :

Contoh alur kerja HTTP GET dengan alur ketahanan.

Diagram sebelumnya menggambarkan:

  • ExampleClient mengirim permintaan HTTP GET ke /comments titik akhir.
  • HttpResponseMessage dievaluasi:
    • Jika respons berhasil (HTTP 200), respons dikembalikan.
    • Jika respons tidak berhasil (HTTP non-200), alur ketahanan menggunakan strategi ketahanan yang dikonfigurasi.

Meskipun ini adalah contoh sederhana, ini menunjukkan bagaimana strategi ketahanan dapat digunakan untuk menangani kesalahan sementara. Untuk informasi selengkapnya, lihat dokumen Polly: Strategi.

Masalah umum

Bagian berikut merinci berbagai masalah yang diketahui.

Kompatibilitas dengan Grpc.Net.ClientFactory paket

Jika Anda menggunakan Grpc.Net.ClientFactory versi atau yang 2.63.0 lebih lama, mengaktifkan penangan ketahanan standar atau hedging untuk klien gRPC dapat menyebabkan pengecualian runtime. Secara khusus, pertimbangkan sampel kode berikut:

services
    .AddGrpcClient<Greeter.GreeterClient>()
    .AddStandardResilienceHandler();

Kode sebelumnya menghasilkan pengecualian berikut:

System.InvalidOperationException: The ConfigureHttpClient method is not supported when creating gRPC clients. Unable to create client with name 'GreeterClient'.

Untuk mengatasi masalah ini, sebaiknya tingkatkan ke Grpc.Net.ClientFactory versi atau yang lebih 2.64.0 baru.

Ada pemeriksaan waktu build yang memverifikasi apakah Anda menggunakan Grpc.Net.ClientFactory versi 2.63.0 atau yang lebih lama, dan jika Anda adalah pemeriksaan akan menghasilkan peringatan kompilasi. Anda dapat menekan peringatan dengan mengatur properti berikut dalam file proyek Anda:

<PropertyGroup>
  <SuppressCheckGrpcNetClientFactoryVersion>true</SuppressCheckGrpcNetClientFactoryVersion>
</PropertyGroup>

Kompatibilitas dengan .NET Application Insights

Jika Anda menggunakan .NET Application Insights, mengaktifkan fungsionalitas ketahanan dalam aplikasi Anda dapat menyebabkan semua telemetri Application Insights hilang. Masalah ini terjadi ketika fungsionalitas ketahanan didaftarkan sebelum layanan Application Insights. Pertimbangkan sampel berikut yang menyebabkan masalah:

// At first, we register resilience functionality.
services.AddHttpClient().AddStandardResilienceHandler();

// And then we register Application Insights. As a result, Application Insights doesn't work.
services.AddApplicationInsightsTelemetry();

Masalah ini disebabkan oleh bug berikut di Application Insights dan dapat diperbaiki dengan mendaftarkan layanan Application Insights sebelum fungsionalitas ketahanan, seperti yang ditunjukkan di bawah ini:

// We register Application Insights first, and now it will be working correctly.
services.AddApplicationInsightsTelemetry();
services.AddHttpClient().AddStandardResilienceHandler();