Retry Policy у мобилном развоју — суштина, стратегије и принцип

Аутор: IT Sectr Објављено: 2026-03-11 Време читања: 10 мин

Retry Policy — политика поновних захтева — скуп правила који одређују када и како мобилна апликација аутоматски понавља неуспеле мрежне позиве. При нестабилној вези или привременим грешкама сервера, добро осмишљена политика понављања повећава поузданост апликације без учешћа корисника. Према истраживању Google Developer Relations (2025), правилна имплементација Retry Policy смањује проценат изгубљених захтева за 40-60% у мобилним апликацијама са честим мрежним операцијама.

Главне тачке

  • Retry Policy — стратегија аутоматског понављања захтева при мрежним грешкама или привременим грешкама сервера.
  • Exponential backoff — метода повећања кашњења између понављања ради смањења оптерећења сервера.
  • Jitter — случајно одступање кашњења које спречава ефекат „стада" (thundering herd).
  • Идемпотентност — кључни захтев за безбедна понављања: поновљени захтев не сме да изазива нежељене ефекте.
  • Circuit Breaker — механизам заустављања понављања при дуготрајној недоступности услуге ради очувања ресурса.

Шта је Retry Policy?

Retry Policy — софтверска стратегија која одређује понашање клијента при неуспеху мрежног захтева: које грешке понављати, колико пута, са којим кашњењем и када прекинути покушаје. У мобилним апликацијама политика понављања је критично важна због нестабилности мобилних мрежа и могућих привремених грешака на страни сервера.

Основна Retry Policy укључује три параметра: максимални број понављања (maxRetries), почетно кашњење (baseDelay) и стратегију повећања кашњења (backoff strategy). Додатно се може задати листа HTTP статусних кодова на које треба реаговати понављањем и временско ограничење за прекид свих покушаја.

Према књизи „Designing Data-Intensive Applications" Мартина Клепмана, 50% грешака у дистрибуираним системима су привремене и отклањају се поновним покушајем. То чини Retry Policy једним од најефикаснијих и најјефтинијих начина за повећање отпорности на грешке мобилне апликације без промена серверске архитектуре.

Које грешке вреди понављати

Привремене грешке (retriable) — једина врста грешака на које Retry Policy треба да реагује. Оне укључују временска прекорачења везе (SocketTimeoutException), привремену недоступност сервера (HTTP 503, 502) и DNS грешке. Сталне грешке — HTTP 400, 401, 403, 404 — бесмислено је понављати, јер указују на проблем у захтеву, а не у мрежи или серверу.

Према истраживању AWS Architecture Blog, правилна класификација грешака на retriable и non-retriable је најважнија одлука при пројектовању Retry Policy. Понављање неидемпотентног захтева са HTTP 401 може довести до блокирања налога, а понављање HTTP 400 до стварања дупликата података. Увек конфигуришите листу кодова за понављање експлицитно.

Основне стратегије понављања

Fixed interval — најједноставнија стратегија: свако понављање се извршава после истог временског интервала. На пример, са кашњењем од 2 секунде, апликација понавља захтев после 2, 2, 2 секунде. Fixed interval је једноставан за имплементацију и предвидљив, али ствара равномерно оптерећење сервера при масовним грешкама.

Incremental interval — кашњење се линеарно повећава са сваким понављањем: прво понављање после 1 секунде, друго после 2, треће после 3 и тако даље. Ова стратегија даје серверу више времена за опоравак при понављајућим грешкама, али је и даље предвидљива за многе клијенте који истовремено отказују.

СтратегијаФормула кашњењаКумулативно време (3 покушаја)Примена
Fixeddelay = D3 × DЈедноставни сценарији, локална временска прекорачења
Incrementaldelay = N × D6 × DПостепено смањење оптерећења
Exponentialdelay = D × 2^N7 × DМасовне грешке, облачне услуге
Exponential + Jitterdelay = random(0, D × 2^N)променљивоВисоко оптерећење, микроуслуге

Избор стратегије зависи од природе апликације. За позадинске задатке синхронизације података на мобилним уређајима, стратегија exponential са jitter-ом је оптимална — даје највећу вероватноћу успеха уз минимално оптерећење сервера и уређаја корисника.

Exponential Backoff и Jitter

Exponential backoff — стратегија у којој се кашњење између понављања удвостручује са сваким покушајем. Ако је почетно кашњење 1 секунда, низ кашњења ће бити 1, 2, 4, 8, 16 секунди. Ово даје серверу експоненцијално растуће време за опоравак.

Jitter — случајно одступање кашњења које спречава синхроне поновне захтеве од више клијената (проблем thundering herd). Без jitter-а, хиљаду клијената са истом Retry Policy ће понављати захтеве истовремено, стварајући вршно оптерећење сервера. Jitter распоређује понављања у времену.

Имплементација у Kotlin-у са корутинама

Корутине Kotlin-а омогућавају имплементацију exponential backoff-а са jitter-ом без блокирања главне нити. Функција retry из kotlinx-coroutines прихвата услов понављања и блок са телом захтева, аутоматски управљајући кашњењима и бројем покушаја.

kotlin
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!!)
}

Функција executeWithRetry прихвата ламбда израз са мрежним позивом и извршава га са exponential backoff-ом и jitter-ом. Ако грешка није подложна понављању (није retriable) или је прекорачен максимални број покушаја, функција враћа грешку. Кашњење се множи случајним коефицијентом од 0.5 до 1.5 за равномерну расподелу понављања.

Circuit Breaker и одустајање од понављања

Circuit Breaker — пројектни образац који спречава бесконачне поновне захтеве при дуготрајној недоступности услуге. Када број грешака пређе праг, Circuit Breaker прелази у стање OPEN и одмах враћа грешку без извршавања захтева, дајући серверу време за опоравак.

У мобилним апликацијама Circuit Breaker је посебно користан при недоступности API-ја због планираног одржавања или мрежних грешака оператора. Без њега, апликација ће трошити батерију и саобраћај на бесконачне поновне покушаје, погоршавајући корисничко искуство и скраћујући време рада уређаја на батерију.

Шема стања Circuit Breaker-а

Три стања Circuit Breaker-а: CLOSED (нормалан рад, захтеви се извршавају), OPEN (одбијање, захтеви се блокирају) и HALF_OPEN (пробни захтев за проверу опоравка). Након одређеног временског периода у стању OPEN, прекидач прелази у HALF_OPEN и извршава један захтев — при успеху се враћа у CLOSED, при неуспеху — у OPEN.

kotlin
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
        }
    }
}

Имплементација Circuit Breaker у Kotlin-у садржи бројач грешака и тајмер за опоравак. Метода protect проверава тренутно стање пре извршавања блокираног захтева и ажурира бројач грешака при грешкама. Након достизања прага failureThreshold, сви захтеви се одмах одбијају до истека timeoutMs.

Retry Policy у мобилним апликацијама

Мобилне мреже имају карактеристике које чине Retry Policy посебно важном. Пребацивање између Wi-Fi и мобилних података, губитак сигнала у метроу и тунелима, привремена блокирања на нивоу оператера — сви ови сценарији доводе до грешака захтева које се успешно решавају поновним покушајима.

На Android-у, библиотека Retrofit и OkHttp пружају уграђени механизам RetryPolicy кроз Interceptor. На iOS-у, проблем се решава кроз URLSessionConfiguration и прилагођено делегирање. За међуплатформски развој, Ktor (KMP) садржи уграђену подршку за retry са подесивим стратегијама.

Имплементација на iOS-у са Combine

Combine — Apple-ов оквир за реактивно програмирање. Оператор retry у Combine-у понавља publisher одређени број пута при грешци, али не дозвољава подешавање кашњења између понављања. За потпуну Retry Policy користи се прилагођена комбинација catch и flatMap са кашњењем.

swift
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()
    }
}

Проширење retryWithBackoff за Publisher у Combine-у имплементира exponential backoff кроз рекурзивни позив са смањењем бројача и удвостручавањем кашњења. Оператор delay ствара паузу између понављања, а catch хвата грешку и одлучује да ли да покуша поново или да врати failure.

Типичне грешке при Retry Policy

Прва грешка — понављање захтева без провере идемпотентности. Ако је сервер креирао ресурс, али није вратио потврду због мрежне грешке, поновљени захтев ће креирати дупликат. За POST захтеве увек користите идемпотентни кључ (Idempotency-Key) у заглављу или пређите на retry само за GET, PUT и DELETE.

Друга грешка — бесконачна понављања (retry forever). Увек постављајте максимални број покушаја (3-5 за мобилне апликације) и укупно временско ограничење за све покушаје. Бесконачна понављања празне батерију и стварају паразитско оптерећење сервера, посебно при миграцији база података или промени API-ја.

Трећа грешка — игнорисање контекста апликације. Ако је корисник затворио апликацију или прешао у позадински режим, активне Retry Policy треба правилно отказати. Користите корутине са SupervisorScope или Combine са животним циклусом UI за аутоматско отказивање понављања при затварању екрана.

Четврта грешка — невођење логова поновљених покушаја. Без логовања нећете знати колико захтева је поновљено, које грешке су се јављале и колико је ефикасна ваша Retry Policy. Додајте метрике: број понављања, успешност после понављања, расподелу кашњења. Ови подаци ће помоћи да подесите оптималне параметре стратегије за конкретну апликацију.

Често постављана питања

Колико пута понављати захтев у мобилној апликацији?

Оптимални број понављања — 3-5 покушаја за већину сценарија. За позадинску синхронизацију дозвољено је 5-7 покушаја, за интерактивне захтеве (нпр. слање формулара) — не више од 3. Већи број понављања не повећава вероватноћу успеха, али троши батерију и саобраћај корисника.

Шта је exponential backoff једноставним речима?

Exponential backoff — удвостручавање кашњења између поновних покушаја: 1 секунда, 2, 4, 8, 16 и тако даље. Ако је сервер преоптерећен, кратка пауза између првих понављања му омогућава да брзо одговори, а растућа пауза са сваким следећим покушајем даје серверу све више времена за опоравак.

Које HTTP статусне кодове вреди понављати?

Понављајте само привремене грешке: 408 (Request Timeout), 429 (Too Many Requests), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout). Грешке 4xx (осим 408 и 429) указују на проблеме клијента — понављање их нема смисла и може бити опасно за податке корисника.

Чиме се Retry Policy разликује од Circuit Breaker-а?

Retry Policy управља понављањем једног захтева при грешци. Circuit Breaker управља стањем везе са услугом: при нагомилавању грешака отвара коло (OPEN) и не дозвољава нове захтеве. Retry ради на нивоу појединачног позива, Circuit Breaker — на нивоу интеграције са услугом.

Како тестирати Retry Policy на мобилним уређајима?

За тестирање Retry Policy користите NetworkInterceptor (OkHttp) на Android-у и URLProtocol (URLSession) на iOS-у за симулацију мрежних грешака. Подесите параметре: учесталост грешака, време недоступности и кодове одговора. Јединични тестови са MockWebServer (OkHttp) или OHHTTPStubs (iOS) проверавају логику понављања без стварне мреже.

Закључак

  • Retry Policy — стратегија аутоматског понављања мрежних захтева при привременим грешкама са подесивим параметрима кашњења и броја покушаја.
  • Exponential backoff са jitter-ом — основна стратегија за мобилне апликације која смањује оптерећење сервера при масовним грешкама и спречава ефекат thundering herd.
  • Идемпотентност — обавезан услов за безбедно понављање не-GET захтева: без ње понављање ствара дупликате података или нежељене споредне ефекте.
  • Circuit Breaker допуњује Retry Policy, спречавајући бесконачна понављања при дуготрајној недоступности услуге и штедећи ресурсе уређаја.
  • Класификација грешака — подела на retriable (503, 502, timeout) и non-retriable (400, 401, 403) је критична за исправан рад политике понављања.
  • Максимално 3-5 понављања у интерактивним сценаријима и до 7 за позадинску синхронизацију — оптимална вредност за мобилне апликације према Google Developer Relations.
  • Препорука — имплементирајте Retry Policy са exponential backoff, Circuit Breaker и логовањем за све мрежне захтеве у мобилној апликацији.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође