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 menggunakanConfigureOrderedGroups
metode untuk mengonfigurasi grup yang diurutkan. -
EndpointGroup
Menambahkan keorderedGroup
yang merutekan 3% permintaan kehttps://example.net/api/experimental
titik akhir dan 97% permintaan kehttps://example.net/api/stable
titik akhir. -
IRoutingStrategyBuilder
Mengonfigurasi untuk menggunakanConfigureWeightedGroups
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 menggunakanConfigureWeightedGroups
metode untuk mengonfigurasi grup tertimbang. - Mengatur ke
SelectionMode
WeightedGroupSelectionMode.EveryAttempt
. -
WeightedEndpointGroup
Menambahkan keweightedGroup
yang merutekan 33% permintaan kehttps://example.net/api/a
titik akhir, 33% permintaan kehttps://example.net/api/b
titik akhir, dan 33% permintaan kehttps://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 kepipelineName
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
danTooManyRequests
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 kepipelineName
kontainer layanan. - Mengaktifkan muatan
"AdvancedPipeline"
ulang alur setiap kali opsi bernamaRetryStrategyOptions
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:
- IServiceProvider Membangun dari ServiceCollection.
-
ExampleClient
Menyelesaikan dari IServiceProvider. -
GetCommentsAsync
Memanggil metode padaExampleClient
untuk mendapatkan komentar. - Menulis setiap komentar ke konsol.
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 :
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();