Retry und exponentielles Backoff: Wiederholen, ohne zu überlasten
Kurz: Eine fehlgeschlagene Anfrage erneut versuchen, aber mit wachsenden Pausen und etwas Zufall, damit ein belasteter Dienst nicht noch stärker unter Druck gerät.
Genauer: In verteilten Systemen scheitern Aufrufe oft nur kurz: Netzfehler, überlasteter Server, Neustart. Ein erneuter Versuch hilft dann. Wiederholt aber jeder Client sofort und ständig, verstärkt sich das Problem (Retry-Storm). Deshalb wartet man zwischen den Versuchen immer länger und verteilt die Zeitpunkte zufällig.
Im Detail
Exponentielles Backoff
Die Wartezeit verdoppelt sich: 1 s, 2 s, 4 s, 8 s … bis zu einem Höchstwert. Dadurch bekommt ein überlasteter Dienst Luft zum Erholen. Eine Obergrenze bei der Zahl der Versuche (zum Beispiel 5) verhindert endloses Warten.
Jitter
Ohne Zufall würden alle Clients, die gleichzeitig scheiterten, auch gleichzeitig wieder anfragen. Jitter mischt Zufall in die Wartezeit, etwa wartezeit = zufall(0, min(max, basis * 2^versuch)) („Full Jitter“). So verteilt sich die Last.
Beispiel in JavaScript
async function mitRetry(fn, versuche = 5) {
for (let i = 0; ; i++) {
try { return await fn(); }
catch (e) {
if (i >= versuche - 1 || !wiederholbar(e)) throw e;
const warte = Math.random() * Math.min(10000, 500 * 2 ** i);
await new Promise((r) => setTimeout(r, warte));
}
}
}Wann nicht wiederholen
Nur vorübergehende Fehler (Zeitüberschreitung, 429, 502, 503) sind wiederholbar. Bei 400 oder 404 bringt ein Wiederholen nichts. Nicht idempotente Aktionen (Zahlung auslösen) dürfen nur wiederholt werden, wenn ein Idempotenz-Schlüssel doppelte Ausführung verhindert. Bei 429 oder 503 gibt der Server oft einen Retry-After-Header vor, der Vorrang hat. Ergänzend schützt ein Circuit Breaker, der nach vielen Fehlern vorübergehend gar nicht mehr anfragt.
Siehe auch: Circuit Breaker und Backpressure, Rate Limiting, REST-API-Design