Ratebegrænsning, retries og tidsbegrænsere
Kombinér ratebegrænsere, retries og timeouts for at styre belastningen og begrænse kaskaderende fejl.
Ratebegrænsning, retries og tidsbegrænsere er en gratis Komplet guide til Spring Boot 4-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Komplet guide til Spring Boot 4, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Komplet guide til Spring Boot 4-kurset indeholder 4 lektioner i alt.
Tre greb til at forme belastningen
Kredsløbsafbrydere stopper kald til en død afhængighed, men de er kun ét værktøj i værktøjskassen til robusthed. For at forme indgående belastning og begrænse kaskadefejl kombinerer du tre andre Resilience4j-byggesten:
- RateLimiter — begrænser, hvor mange kald pr. tidsvindue der får lov til at starte. Overskydende kaldere venter eller afvises.
- Retry — kalder et fejlet kald igen et begrænset antal gange, helst med backoff, for at komme igennem midlertidige fejl.
- TimeLimiter — begrænser, hvor længe et enkelt kald må køre, før det annulleres, så tråden frigives, og latenstiden i halen begrænses.
Brugt sammen beskytter de både dig (så du ikke overbelaster din egen tjeneste) og din underliggende tjeneste (så du ikke bombarderer en presset afhængighed).
Tilføjelse af Resilience4j til Spring Boot 4
Spring Boot 4 integrerer Resilience4j via Spring Cloud Circuit Breaker / resilience4j-spring-boot3-startpakken, som stiller aspektbaseret funktionalitet til rådighed for hver byggesten.
Tilføj startpakken og AOP-understøttelse, så annoteringerne flettes ind:
resilience4j-spring-boot3leverer aspekter forRateLimiter,Retry,TimeLimiter,CircuitBreakerogBulkhead.spring-boot-starter-aoper påkrævet — annoteringerne er implementeret som AOP-rådgivning.- Målepunkter føres automatisk ind i Micrometer, når Actuator er til stede.
<dependencies>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>RateLimiter: Begrænsning af kald pr. tidsvindue
En RateLimiter opdeler tiden i opdateringsperioder. Hver periode tildeler et fast antal tilladelser. En kaldende part, der ikke finder en ledig tilladelse, venter i op til timeoutDuration; hvis der stadig ikke er nogen, fejler kaldet hurtigt med RequestNotPermitted.
Konfigurér instanser i application.yml under resilience4j.ratelimiter:
limitForPeriod— antal tilladelser, der tildeles i hver opdateringsperiode.limitRefreshPeriod— hvor ofte antallet af tilladelser nulstilles.timeoutDuration— hvor længe en kaldende part blokerer og venter på en tilladelse, før den afvises.
resilience4j:
ratelimiter:
instances:
pricingApi:
limitForPeriod: 50
limitRefreshPeriod: 1s
timeoutDuration: 200ms
registerHealthIndicator: trueAnvendelse af @RateLimiter
Annotér den metode, der kalder den beskyttede ressource. name skal svare til instansen i YAML. Når grænsen overskrides, og ingen tilladelse bliver ledig inden for timeoutDuration, kaster Resilience4j RequestNotPermitted — send den til en reservefunktion, så klienterne får et kontrolleret 429 i stedet for en staksporing.
Vigtigt valg: En RateLimiter begrænser dine udgående kald. Den sænker ikke indgående HTTP-trafik af sig selv — kombinér den med en reservefunktion, der signalerer belastningsmodtryk.
@Service
public class PricingClient {
private final RestClient restClient;
public PricingClient(RestClient restClient) {
this.restClient = restClient;
}
@RateLimiter(name = "pricingApi", fallbackMethod = "throttled")
public Quote fetchQuote(String symbol) {
return restClient.get()
.uri("/quotes/{symbol}", symbol)
.retrieve()
.body(Quote.class);
}
private Quote throttled(String symbol, RequestNotPermitted ex) {
throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS,
"Pricing rate limit exceeded, retry shortly");
}
}Retry: Håndtering af midlertidige fejl
En Retry kalder et fejlet kald igen op til maxAttempts gange. Det er kun korrekt ved midlertidige fejl — nulstillede forbindelser, 503-svar og korte timeouts — og kun sikkert ved idempotente handlinger. Hvis du gentager en ikke-idempotent POST, kan en kunde blive opkrævet to gange.
Brug eksponentiel backoff for at undgå synkroniserede gentagelsesstorme, og angiv tydeligt, hvilke undtagelser der udløser en gentagelse, og hvilke der straks afbryder:
retryExceptions— kun disse udløser en gentagelse.ignoreExceptions— disse afbryder straks (f.eks. 4xx-klientfejl).enableExponentialBackoffmed en multiplikator spreder forsøgene ud.
resilience4j:
retry:
instances:
pricingApi:
maxAttempts: 3
waitDuration: 200ms
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
retryExceptions:
- java.io.IOException
- org.springframework.web.client.HttpServerErrorException
ignoreExceptions:
- org.springframework.web.client.HttpClientErrorExceptionEksponentiel backoff med jitter, begrebsmæssigt
Eksponentiel backoff multiplicerer ventetiden efter hvert forsøg: 200ms, 400ms, 800ms ... Men hvis tusindvis af klienter fejler på samme tidspunkt, anvender de alle backoff i takt og samles igen — en gentagelsesstorm. Ved at tilføje jitter (tilfældig ventetid) bliver de afsynkroniseret.
Dette selvstændige program modellerer ventetidsplanen, så du kan se spredningen i forsinkelserne ved backoff med jitter, og det kan køres af en onlinebedømmer uden et framework:
import java.util.concurrent.ThreadLocalRandom;
public class BackoffDemo {
static long backoffWithJitter(int attempt, long baseMillis, double multiplier) {
double exp = baseMillis * Math.pow(multiplier, attempt - 1);
long jitter = ThreadLocalRandom.current().nextLong((long) (exp / 2) + 1);
return (long) (exp / 2) + jitter; // half fixed, half random
}
public static void main(String[] args) {
for (int attempt = 1; attempt <= 4; attempt++) {
long wait = backoffWithJitter(attempt, 200, 2.0);
System.out.println("Attempt " + attempt + " -> wait ~" + wait + "ms");
}
}
}Anvendelse af @Retry
Sæt @Retry på den samme metode. Rækkefølgen er vigtig, når annoteringer kombineres: Resilience4j anvender aspekter i rækkefølgen Retry → CircuitBreaker → RateLimiter → TimeLimiter → Bulkhead (yderst til inderst), så en gentagelse omslutter det hastighedsbegrænsede kald, og hvert forsøg indhenter en tilladelse igen.
Reservefunktionen modtager den endelige undtagelse, når alle forsøg er opbrugt:
@Service
public class PricingClient {
@Retry(name = "pricingApi")
@RateLimiter(name = "pricingApi", fallbackMethod = "throttled")
public Quote fetchQuote(String symbol) {
return restClient.get()
.uri("/quotes/{symbol}", symbol)
.retrieve()
.body(Quote.class);
}
private Quote throttled(String symbol, Throwable ex) {
// Returns a last-known-good or default after retries + limit exhausted
return Quote.unavailable(symbol);
}
}TimeLimiter: Begrænsning af latenstid i halen
En TimeLimiter begrænser, hvor længe et enkelt asynkront kald må køre. Den fungerer kun med futures — metoden skal returnere CompletableFuture (eller en anden understøttet asynkron type), så Resilience4j kan annullere kaldet, når tidsfristen udløber.
Dette er modgiften mod langsomme afhængigheder: En gentagelse håndterer fejl, men et kald, der blot hænger i 30 sekunder, opbruger din trådpulje. TimeLimiter omdanner et hængende kald til en hurtig TimeoutException.
timeoutDuration— tidsfristen pr. kald.cancelRunningFuture— afbryd den kørende opgave ved timeout, så dens tråd frigives.
resilience4j:
timelimiter:
instances:
pricingApi:
timeoutDuration: 2s
cancelRunningFuture: trueAnvendelse af @TimeLimiter på en asynkron metode
@TimeLimiter kræver, at metoden returnerer en CompletableFuture. Uden en asynkron returtype gør aspektet stiltiende ingenting. Kombinér den med en CircuitBreaker, så gentagne timeouts til sidst åbner afbryderen og stopper spildte ressourcer.
Bemærk, at reservefunktionen også returnerer en CompletableFuture — dens signatur skal svare til den beskyttede metodes returtype:
@Service
public class PricingClient {
@TimeLimiter(name = "pricingApi")
@CircuitBreaker(name = "pricingApi", fallbackMethod = "timedOut")
public CompletableFuture<Quote> fetchQuoteAsync(String symbol) {
return CompletableFuture.supplyAsync(() ->
restClient.get()
.uri("/quotes/{symbol}", symbol)
.retrieve()
.body(Quote.class));
}
private CompletableFuture<Quote> timedOut(String symbol, Throwable ex) {
return CompletableFuture.completedFuture(Quote.unavailable(symbol));
}
}Korrekt rækkefølge for alle tre
Når du kombinerer alle tre, bestemmer aspektets rækkefølge adfærden. Resilience4j's dokumenterede standardrækkefølge for udsmykning, fra yderst til inderst, er:
Retry( CircuitBreaker( RateLimiter( TimeLimiter( Bulkhead( call ) ) ) ) )
- Retry yderst, så en gentagelse kører hele den beskyttede kæde igen, kontrollerer kredsløbsafbryderen igen og indhenter en tilladelse fra hastighedsbegrænseren ved hvert forsøg.
- TimeLimiter inderst, så hvert enkelt forsøg har sin egen tidsfrist — en gentagelse af et kald, der fik timeout, får et nyt ur.
- Hvis du placerer Retry inde i RateLimiter, kan ét logisk kald bruge flere tilladelser pr. forsøg — normalt forkert, og det kan udsulte andre kaldere.
Med annoteringer behøver du ikke ordne dem manuelt; Resilience4j håndhæver rækkefølgen via aspektprioritet.
Overvågning og justering via Actuator
Du kan ikke justere noget, du ikke kan se. Når Actuator er på klassestien, offentliggør hver instans Micrometer-målepunkter og sundhedsindikatorer. Eksponér dem, og hold øje med de vigtigste signaler:
resilience4j_ratelimiter_available_permissions— hvis denne værdi er nul, begrænser du reel trafik; hæv grænsen, eller skaler ud.resilience4j_retry_callsmærket medkind=successful_with_retrykontrafailed_with_retry— mange fejlede gentagelser betyder, at gentagelserne ikke hjælper; fejlen er ikke midlertidig.resilience4j_timelimiter_callsmærket medkind=timeout— stigende timeouts signalerer en forringet afhængighed, før afbryderen overhovedet åbner.
management:
endpoints:
web:
exposure:
include: health, metrics, ratelimiters, retries
metrics:
tags:
application: tracing-service
health:
ratelimiters:
enabled: trueHurtigt tjek: Valg af den rette byggesten
Et downstream-pris-API hænger lejlighedsvis i over 30 sekunder i stedet for at returnere en fejl, og disse hængende kald opbruger din tjenestes anmodningstråde. Hvilken Resilience4j-byggesten håndterer mest direkte netop denne fejltilstand?
Opsummering: Formning af belastning og begrænsning af fejl
Du kombinerede tre supplerende primitiver for at forme belastningen og begrænse kaskaderende fejl:
- RateLimiter begrænser antallet af kald pr. tidsvindue og afviser overskydende kald med
RequestNotPermitted, så du aldrig overbelaster dig selv eller en downstream-tjeneste. - Retry håndterer forbigående fejl i idempotente kald ved hjælp af eksponentiel backoff plus jitter for at undgå genforsøgsoverwhelm.
- TimeLimiter begrænser halelatens i asynkrone kald (
CompletableFuture) og omdanner hængende kald til hurtige timeout-fejl, så tråde frigives.
Stabl dem ved hjælp af annotationer; Resilience4j håndhæver rækkefølgen Retry → CircuitBreaker → RateLimiter → TimeLimiter → Bulkhead, så et genforsøg kører hele den beskyttede kæde igen, mens hvert forsøg har sin egen tidsfrist. Kobl altid reservefunktioner på for en gradvis forringelse, og hold øje med Actuator-/Micrometer-målinger for at justere grænserne efter den virkelige trafik.
Lær Java med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 21
- Lektioner
- 84
Ofte stillede spørgsmål
Er lektionen “Ratebegrænsning, retries og tidsbegrænsere” gratis?
Ja — alle 3 lektioner i læringssporet Komplet guide til Spring Boot 4, inklusive “Ratebegrænsning, retries og tidsbegrænsere”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Komplet guide til Spring Boot 4-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Ratebegrænsning, retries og tidsbegrænsere”?
Kombinér ratebegrænsere, retries og timeouts for at styre belastningen og begrænse kaskaderende fejl. Du øver dig i Komplet guide til Spring Boot 4 med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Komplet guide til Spring Boot 4?
Der kræves ingen tidligere erfaring. Komplet guide til Spring Boot 4 på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.
Hvor lang tid tager lektionen “Ratebegrænsning, retries og tidsbegrænsere”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Komplet guide til Spring Boot 4-lektion?
Ja. Alle Komplet guide til Spring Boot 4-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Kontekstvideregivelse og span-instrumentering
- Circuit breakers og bulkhead-isolering
- Ratebegrænsning, retries og tidsbegrænsere
- Sammenkædning af logs, metrikker og traces