Retry Policy — az ismételt kérések politikája — szabályok halmaza, amely meghatározza, hogy mikor és hogyan ismétel meg egy mobilalkalmazás automatikusan sikertelen hálózati hívásokat. Instabil kapcsolat vagy ideiglenes szerverhibák esetén a jól megtervezett ismétlési politika növeli az alkalmazás megbízhatóságát a felhasználó részvétele nélkül. A Google Developer Relations (2025) kutatása szerint a Retry Policy helyes implementációja 40-60%-kal csökkenti az elveszett kérések arányát a gyakori hálózati műveletekkel rendelkező mobilalkalmazásokban.
Főbb pontok
Retry Policy — egy szoftverstratégia, amely meghatározza az ügyfél viselkedését egy hálózati kérés meghiúsulásakor: mely hibákat ismételje meg, hányszor, milyen késleltetéssel és mikor hagyja abba a próbálkozásokat. Mobilalkalmazásokban az ismétlési politika kritikus fontosságú a mobilhálózatok instabilitása és a szerveroldali lehetséges ideiglenes meghibásodások miatt.
Az alap Retry Policy három paramétert foglal magában: a maximális ismétlések számát (maxRetries), a kezdeti késleltetést (baseDelay) és a késleltetés növelésének stratégiáját (backoff strategy). Ezenkívül megadható a HTTP státuszkódok listája, amelyekre ismétléssel kell reagálni, valamint egy időtúllépés az összes próbálkozás megszakításához.
Martin Kleppmann „Designing Data-Intensive Applications" című könyve szerint az elosztott rendszerekben előforduló hibák 50%-a ideiglenes jellegű, és egy újbóli próbálkozással kiküszöbölhető. Ez teszi a Retry Policy-t az egyik leghatékonyabb és legolcsóbb módszerré a mobilalkalmazás hibahatékonyságának növelésére a szerverarchitektúra megváltoztatása nélkül.
Ideiglenes hibák (retriable) — az egyetlen meghibásodási típus, amelyre a Retry Policy-nak reagálnia kell. Ezek közé tartoznak a kapcsolati időtúllépések (SocketTimeoutException), a szerver ideiglenes elérhetetlensége (HTTP 503, 502) és a DNS-hibák. Az állandó hibák — HTTP 400, 401, 403, 404 — ismétlése értelmetlen, mert a kérésben lévő problémára utalnak, nem a hálózatban vagy a szerverben.
Az AWS Architecture Blog kutatása szerint a hibák helyes osztályozása retriable és non-retriable kategóriákba a legfontosabb döntés a Retry Policy tervezésekor. Egy nem idempotens HTTP 401 kérés ismétlése a fiók blokkolásához, a HTTP 400 ismétlése pedig adatduplikátumok létrehozásához vezethet. Mindig explicit módon konfigurálja az ismétlendő kódok listáját.
Fixed interval — a legegyszerűbb stratégia: minden ismétlés ugyanazon időintervallum után történik. Például 2 másodperces késleltetés esetén az alkalmazás 2, 2, 2 másodperc után ismétli meg a kérést. A Fixed interval egyszerűen implementálható és kiszámítható, de tömeges meghibásodások esetén egyenletes terhelést hoz létre a szerveren.
Incremental interval — a késleltetés lineárisan növekszik minden ismétléssel: első ismétlés 1 másodperc után, második 2 után, harmadik 3 után, és így tovább. Ez a stratégia több időt ad a szervernek a helyreállásra ismétlődő hibák esetén, de továbbra is kiszámítható sok egyszerre meghibásodó ügyfél számára.
| Stratégia | Késleltetési képlet | Kumulatív idő (3 próbálkozás) | Alkalmazás |
|---|---|---|---|
| Fixed | delay = D | 3 × D | Egyszerű forgatókönyvek, helyi időtúllépések |
| Incremental | delay = N × D | 6 × D | Fokozatos terheléscsökkentés |
| Exponential | delay = D × 2^N | 7 × D | Tömeges hibák, felhőszolgáltatások |
| Exponential + Jitter | delay = random(0, D × 2^N) | változó | Magas terhelés, mikroszolgáltatások |
A stratégia kiválasztása az alkalmazás jellegétől függ. A mobil eszközökön történő adatszinkronizáció háttérfeladataihoz a jitterrel ellátott exponenciális stratégia az optimális — a legnagyobb siker valószínűséget nyújtja minimális szerver- és felhasználói eszközterhelés mellett.
Exponential backoff — az a stratégia, ahol a késleltetés az ismétlések között minden próbálkozással megduplázódik. Ha a kezdeti késleltetés 1 másodperc, a késleltetések sorozata 1, 2, 4, 8, 16 másodperc lesz. Ez exponenciálisan növekvő időt ad a szervernek a helyreálláshoz.
Jitter — a késleltetés véletlenszerű eltérése, amely megakadályozza a szinkron ismételt kéréseket több ügyféltől (thundering herd probléma). Jitter nélkül ezer, azonos Retry Policy-val rendelkező ügyfél egyszerre ismételné meg a kéréseket, csúcsterhelést okozva a szerveren. A Jitter időben szétszórja az ismétléseket.
A Kotlin korutinok lehetővé teszik az exponenciális backoff implementációját jitterrel a főszál blokkolása nélkül. A retry függvény a kotlinx-coroutines-ból elfogadja az ismétlési feltételt és egy blokkot a kérés törzsével, automatikusan kezelve a késleltetéseket és a próbálkozások számát.
suspend fun RetryPolicy.executeWithRetry(
block: suspend () -> Result<T>
): Result<T> {
var lastError: Throwable? = null
repeat(maxRetries + 1) { attempt ->
try {
return block()
} catch (e: Exception) {
if (!isRetriable(e) || attempt == maxRetries) {
return Result.failure(e)
}
val delay = (baseDelayMs * (1 shl attempt))
.toLong()
val jitteredDelay = (delay * (0.5 + Random.nextDouble())).toLong()
delay(jitteredDelay)
lastError = e
}
}
return Result.failure(lastError!!)
}
Az executeWithRetry függvény elfogad egy lambda kifejezést hálózati hívással, és exponenciális backoff-fal és jitterrel hajtja végre. Ha a hiba nem ismételhető (nem retriable), vagy a maximális próbálkozások száma túllépésre került, a függvény hibát ad vissza. A késleltetés megszorzásra kerül egy 0,5 és 1,5 közötti véletlenszerű együtthatóval az ismétlések egyenletes eloszlása érdekében.
Circuit Breaker — egy tervezési minta, amely megakadályozza a végtelen ismételt kéréseket a szolgáltatás hosszan tartó elérhetetlensége esetén. Amikor a hibák száme meghaladja a küszöbértéket, a Circuit Breaker OPEN állapotba lép, és azonnal hibát ad vissza a kérés végrehajtása nélkül, időt adva a szervernek a helyreállásra.
A mobilalkalmazásokban a Circuit Breaker különösen hasznos az API tervezett karbantartás vagy operátori hálózati hibák miatti elérhetetlensége esetén. Nélküle az alkalmazás az akkumulátort és az adatforgalmat végtelen ismételt próbálkozásokra pazarolná, rontva a felhasználói élményt és csökkentve az eszköz akkumulátoros üzemidejét.
Három állapot Circuit Breaker: CLOSED (normál működés, a kérések végrehajtásra kerülnek), OPEN (elutasítás, a kérések blokkolva vannak) és HALF_OPEN (tesztkérés a helyreállás ellenőrzésére). Egy meghatározott időtúllépés után az OPEN állapotban a kapcsoló HALF_OPEN állapotba lép, és végrehajt egy kérést — siker esetén visszatér CLOSED-be, sikertelenség esetén OPEN-be.
class CircuitBreaker(
private val failureThreshold: Int = 3,
private val timeoutMs: Long = 30000
) {
private var state = State.CLOSED
private var failureCount = 0
private var lastFailureTime: Long = 0
suspend fun T.protect(block: suspend () -> T): T {
checkState()
return try {
val result = block()
onSuccess()
result
} catch (e: Exception) {
onFailure()
throw e
}
}
}
A Circuit Breaker implementációja Kotlinban hibaszámlálót és helyreállási időzítőt tartalmaz. A protect metódus ellenőrzi az aktuális állapotot a blokkolt kérés végrehajtása előtt, és frissíti a hibaszámlálót meghibásodások esetén. A failureThreshold küszöb elérése után az összes kérés azonnal elutasításra kerül a timeoutMs lejártáig.
A mobilhálózatok olyan jellemzőkkel rendelkeznek, amelyek különösen fontossá teszik a Retry Policy-t. A Wi-Fi és a mobiladatok közötti váltás, a jelvesztés a metróban és alagutakban, az operátori szintű ideiglenes blokkolások — mindezek a forgatókönyvek olyan kérési hibákhoz vezetnek, amelyeket az ismételt próbálkozások sikeresen kezelnek.
Androidon a Retrofit könyvtár és az OkHttp beépített RetryPolicy mechanizmust biztosít az Interceptoron keresztül. iOS-en a probléma az URLSessionConfiguration és egyéni delegálás segítségével oldódik meg. Cross-platform fejlesztéshez a Ktor (KMP) beépített retry támogatást tartalmaz konfigurálható stratégiákkal.
Combine — az Apple keretrendszere reaktív programozáshoz. A retry operátor a Combine-ban meghatározott számú alkalommal ismétli meg a publisher-t hiba esetén, de nem teszi lehetővé a késleltetés konfigurálását az ismétlések között. A teljes Retry Policy-hez a catch és a flatMap egyéni kombinációját használják késleltetéssel.
extension Publisher {
func retryWithBackoff(
retries: Int = 3,
baseDelay: TimeInterval = 1.0
) -> AnyPublisher<Output, Failure> {
return self.catch { error -> AnyPublisher in
guard retries > 0 else {
return Fail(error).eraseToAnyPublisher()
}
return Just(())
.delay(for: .seconds(baseDelay), scheduler: DispatchQueue.main)
.flatMap { self.retryWithBackoff(
retries: retries - 1,
baseDelay: baseDelay * 2
) }
.eraseToAnyPublisher()
}
.eraseToAnyPublisher()
}
}
A retryWithBackoff kiterjesztés a Publisher-hez a Combine-ban rekurzív hívással implementálja az exponenciális backoff-ot a számláló csökkentésével és a késleltetés megduplázásával. A delay operátor szünetet hoz létre az ismétlések között, a catch pedig elkapja a hibát, és eldönti, hogy újra próbálkozzon-e vagy failure-t adjon vissza.
Első hiba — a kérések ismétlése az idempotencia ellenőrzése nélkül. Ha a szerver létrehozott egy erőforrást, de hálózati hiba miatt nem küldött vissza visszaigazolást, az ismételt kérés duplikátumot hoz létre. POST kérésekhez mindig használjon idempotens kulcsot (Idempotency-Key) a fejlécben, vagy csak GET, PUT és DELETE kérésekre alkalmazza a retry-t.
Második hiba — végtelen ismétlések (retry forever). Mindig állítson be maximális próbálkozási számot (3-5 mobilalkalmazásokhoz) és teljes időtúllépést az összes próbálkozásra. A végtelen ismétlések lemerítik az akkumulátort, és parazita terhelést hoznak létre a szerveren, különösen adatbázis-migráció vagy API-változás esetén.
Harmadik hiba — az alkalmazás kontextusának figyelmen kívül hagyása. Ha a felhasználó bezárta az alkalmazást vagy háttérmódba lépett, az aktív Retry Policy-t megfelelően meg kell szakítani. Használjon korutinokat SupervisorScope-pal vagy Combine-t az UI életciklussal az ismétlések automatikus megszakításához a képernyő bezárásakor.
Negyedik hiba — az ismételt próbálkozások naplózásának elmulasztása. Naplózás nélkül nem fogja tudni, hány kérés lett megismételve, milyen hibák fordultak elő, és mennyire hatékony a Retry Policy-je. Adjon hozzá metrikákat: ismétlések száma, sikeresség ismétlés után, késleltetések eloszlása. Ezek az adatok segítenek beállítani a stratégia optimális paramétereit az adott alkalmazáshoz.
Gyakran ismételt kérdések
Az optimális ismétlések száma — 3-5 próbálkozás a legtöbb forgatókönyvhöz. Háttérszinkronizációhoz 5-7 próbálkozás megengedett, interaktív kérésekhez (pl. űrlap küldése) — legfeljebb 3. Több ismétlés nem növeli a siker valószínűségét, de fogyasztja a felhasználó akkumulátorát és adatforgalmát.
Exponential backoff — a késleltetés megduplázása az ismételt próbálkozások között: 1 másodperc, 2, 4, 8, 16, és így tovább. Ha a szerver túlterhelt, a rövid szünet az első ismétlések között lehetővé teszi számára a gyors válaszadást, a növekvő szünet pedig minden további próbálkozással egyre több időt ad a szervernek a helyreállásra.
Csak az ideiglenes hibákat ismételje: 408 (Request Timeout), 429 (Too Many Requests), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout). A 4xx hibák (kivéve 408 és 429) ügyfélproblémákra utalnak — ismétlésük értelmetlen és veszélyes lehet a felhasználó adataira.
Retry Policy egyetlen kérés ismétlését kezeli hiba esetén. A Circuit Breaker a szolgáltatással való kapcsolat állapotát kezeli: a hibák felhalmozódásakor kinyitja az áramkört (OPEN) és nem enged új kéréseket. A Retry egyedi hívás szintjén működik, a Circuit Breaker — a szolgáltatással való integráció szintjén.
A Retry Policy teszteléséhez használja a NetworkInterceptor-t (OkHttp) Androidon és az URLProtocol-t (URLSession) iOS-en a hálózati hibák szimulálásához. Állítsa be a paramétereket: hibagyakoriság, elérhetetlenség időtartama és válaszkódok. Egységtesztek MockWebServer-rel (OkHttp) vagy OHHTTPStubs-szal (iOS) ellenőrzik az ismétlési logikát valós hálózat nélkül.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is