Pr.BuildingBlocks.Cms.Http
1.1.0
dotnet add package Pr.BuildingBlocks.Cms.Http --version 1.1.0
NuGet\Install-Package Pr.BuildingBlocks.Cms.Http -Version 1.1.0
<PackageReference Include="Pr.BuildingBlocks.Cms.Http" Version="1.1.0" />
<PackageVersion Include="Pr.BuildingBlocks.Cms.Http" Version="1.1.0" />
<PackageReference Include="Pr.BuildingBlocks.Cms.Http" />
paket add Pr.BuildingBlocks.Cms.Http --version 1.1.0
#r "nuget: Pr.BuildingBlocks.Cms.Http, 1.1.0"
#:package Pr.BuildingBlocks.Cms.Http@1.1.0
#addin nuget:?package=Pr.BuildingBlocks.Cms.Http&version=1.1.0
#tool nuget:?package=Pr.BuildingBlocks.Cms.Http&version=1.1.0
Pr.BuildingBlocks.Cms.Http
Odporne klienty HTTP dla mikroserwisów CMS Polskiego Radia. Jeden helper rejestruje typowany
HttpClient z pełnym pipeline'em odporności — timeout, retry i circuit breaker — opartym na
Microsoft.Extensions.Http.Resilience
(Polly v8).
Pakiet jest niezależny od Pr.BuildingBlocks.Cms.Infrastructure — ciągnie tylko jedną zależność
(Microsoft.Extensions.Http.Resilience). Serwisy bez wychodzących wywołań HTTP nie muszą go referencjować.
- Target framework:
net8.0 - PackageId:
Pr.BuildingBlocks.Cms.Http - Wersja:
1.1.0
Spis treści
- Instalacja
- Quick start
- Konfiguracja per serwis
- Domyślne wartości
- Dlaczego MinimumThroughput = 10
- Retry a idempotentność
- Obserwowalność
- Migracja z lokalnego helpera
Instalacja
<PackageReference Include="Pr.BuildingBlocks.Cms.Http" Version="1.1.0" />
Pakiet ciągnie tranzytywnie Microsoft.Extensions.Http.Resilience (9.1.0) — usuń własny,
bezpośredni PackageReference do tej paczki po migracji.
Quick start
W warstwie Infrastructure (np. ExternalServices/Extensions.cs):
using Pr.BuildingBlocks.Cms.Http;
internal static class Extensions
{
public static IServiceCollection AddExternalServices(
this IServiceCollection services, IConfiguration configuration)
{
var options = configuration.GetRequiredSection(ExternalClientsOptions.SectionName)
.Get<ExternalClientsOptions>()
?? throw new InvalidOperationException(
$"Konfiguracja '{ExternalClientsOptions.SectionName}' jest wymagana.");
services.AddResilientHttpClient<IExternalSearchClient, ExternalSearchClient>(options.SearchUrl);
services.AddResilientHttpClient<IQueryServiceClient, QueryServiceClient>(options.QueryUrl);
return services;
}
}
Każdy klient dostaje własny, izolowany circuit breaker. Kolejność strategii w pipeline: rate limiter → total timeout → retry → circuit breaker → attempt timeout.
Konfiguracja per serwis
Drugi (opcjonalny) parametr nadpisuje domyślną politykę — np. związanie z appsettings.json:
services.AddResilientHttpClient<IImagesClient, ImagesClient>(opt.BaseUrl, resilience =>
{
resilience.TotalRequestTimeoutSeconds = opt.TimeoutSeconds;
resilience.MaxRetryAttempts = opt.RetryCount;
resilience.CircuitBreaker.MinimumThroughput = 20;
});
Walidacja wzajemnych zależności (SamplingDuration >= 2 × AttemptTimeout, TotalTimeout > AttemptTimeout)
odbywa się na etapie rejestracji — z czytelnym ArgumentException, zanim handler rzuciłby mniej
zrozumiały błąd przy starcie aplikacji.
Domyślne wartości
| Parametr | Domyślnie | Uwagi |
|---|---|---|
TotalRequestTimeoutSeconds |
30 | Budżet na całość żądania wraz z retry |
AttemptTimeoutSeconds |
10 | Limit pojedynczej próby |
MaxRetryAttempts |
3 | Backoff wykładniczy + jitter (handler); musi być >= 1 |
RetryBaseDelaySeconds |
2 | Bazowe opóźnienie backoffu retry; 0 = retry natychmiastowy |
DisableRetry |
false | Wyłącza ponawianie (klient mutujący — patrz Retry a idempotentność) |
CircuitBreaker.FailureRatio |
0.5 | Udział błędów otwierający obwód |
CircuitBreaker.MinimumThroughput |
10 | Próg żądań w oknie (patrz niżej) |
CircuitBreaker.SamplingDurationSeconds |
30 | Okno oceny FailureRatio |
CircuitBreaker.BreakDurationSeconds |
5 | Czas otwartego obwodu (fail-fast) |
HttpClient.Timeout jest ustawiany na Timeout.InfiniteTimeSpan — budżetem czasu zarządza
strategia TotalRequestTimeout z pipeline'u, dzięki czemu retry nie są ucinane przedwcześnie
przez timeout samego HttpClient.
Dlaczego MinimumThroughput = 10
Domyślny próg Microsoftu to 100 żądań w oknie 30 s, zanim breaker w ogóle może się otworzyć. Wewnętrzny CMS redakcyjny rzadko osiąga taki ruch na pojedynczą zależność, więc przy domyślnych progach circuit breaker bywa martwy — skonfigurowany, ale nigdy nie wyzwalany.
Obniżenie progu do 10 sprawia, że obwód jest realnie osiągalny przy ruchu redakcyjnym.
Towarzyszące podniesienie FailureRatio do 0.5 chroni przed otwieraniem obwodu na pojedynczy
przejściowy błąd (wymaga połowy nieudanych prób w oknie). Oba progi dostrój per serwis, jeśli masz
realne metryki ruchu.
Retry a idempotentność
⚠️ Retry stosuje się do każdej metody HTTP — także POST. Standardowy handler ponawia
przejściowe błędy (5xx, 408, błąd sieci) niezależnie od czasownika. Dla idempotentnych odczytów
(GET — pobranie obrazu, autora, wyszukiwanie) to bezpieczne. Dla mutujących POST/PUT retry
może podwoić efekt uboczny (np. double-create), jeśli pierwsze żądanie dotarło, a odpowiedź
zginęła.
Dla klienta wykonującego niezabezpieczone operacje mutujące wyłącz retry:
services.AddResilientHttpClient<IPaymentsClient, PaymentsClient>(opt.BaseUrl, resilience =>
{
resilience.DisableRetry = true; // brak retry — circuit breaker i timeouty nadal działają
});
Circuit breaker i limity czasu pozostają aktywne — wyłączasz wyłącznie ponawianie.
Obserwowalność
Zmiany stanu obwodu są logowane przez ILogger (kategoria
Pr.BuildingBlocks.Cms.Http.CircuitBreaker), żeby otwarcie breakera w produkcji nie przeszło
bez śladu:
- Otwarcie →
LogWarningz nazwą klienta, czasem przerwy i przyczyną (kod HTTP / typ wyjątku). - Zamknięcie / half-open →
LogInformation.
warn: Pr.BuildingBlocks.Cms.Http.CircuitBreaker[0]
Circuit breaker dla klienta IImagesClient OTWARTY na 5 s — downstream niedostępny (HTTP 503).
Niezależnie od tego standardowy handler emituje natywną telemetrię Polly (metryki + zdarzenia), którą można podłączyć pod OpenTelemetry / Grafanę. Warning powyżej to „głośny” sygnał do alertowania nawet bez wpiętego stacku metryk.
Migracja z lokalnego helpera
Serwisy, które miały skopiowany prywatny AddResilientHttpClient z AddStandardResilienceHandler:
- Dodaj
PackageReferencedoPr.BuildingBlocks.Cms.Http. - Usuń bezpośredni
PackageReferencedoMicrosoft.Extensions.Http.Resilience(idzie tranzytywnie). - Usuń lokalną prywatną metodę
AddResilientHttpClientiusing ...Http.Resilience. - Dodaj
using Pr.BuildingBlocks.Cms.Http;— sygnatura wywołań pozostaje identyczna.
Serwisy bez polityki odporności (gołe AddHttpClient) oraz pr.authors.cms (retry bez breakera)
zyskują circuit breaker przez tę samą podmianę wywołania.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.Http.Resilience (>= 9.1.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.