Retry Policy nello sviluppo mobile — essenza, strategie e principi

Autore: IT Sectr Pubblicato: 2026-03-11 Tempo di lettura: 10 min

Retry Policy — politica di ripetizione delle richieste — è un insieme di regole che determinano quando e come un'applicazione mobile ripete automaticamente le chiamate di rete fallite. Con connessioni instabili o errori temporanei del server, una politica di ripetizione ben progettata migliora l'affidabilità dell'applicazione senza intervento dell'utente. Secondo una ricerca di Google Developer Relations (2025), l'implementazione corretta della Retry Policy riduce la percentuale di richieste perse del 40–60% nelle applicazioni mobili con frequenti operazioni di rete.

Punti chiave

  • Retry Policy — una strategia di ripetizione automatica delle richieste in caso di guasti di rete o errori temporanei del server.
  • Exponential backoff — un metodo per aumentare il ritardo tra i tentativi per ridurre il carico del server.
  • Jitter — una deviazione casuale del ritardo che previene l'effetto mandria (thundering herd).
  • Idempotenza — un requisito chiave per tentativi sicuri: una richiesta ripetuta non deve causare effetti collaterali.
  • Circuit Breaker — un meccanismo per interrompere i tentativi durante l'indisponibilità prolungata del servizio per conservare le risorse.

Cos'è la Retry Policy?

Retry Policy — una strategia software che definisce il comportamento del cliente in caso di fallimento di una richiesta di rete: quali errori devono essere ripetuti, quante volte, con quale ritardo e quando interrompere i tentativi. Nelle applicazioni mobili, la politica di ripetizione è di importanza critica a causa dell'instabilità delle reti mobili e dei possibili guasti temporanei del server.

Una Retry Policy di base include tre parametri: numero massimo di tentativi (maxRetries), ritardo iniziale (baseDelay) e la strategia di backoff. Inoltre, può essere specificato un elenco di codici di stato HTTP che devono attivare un tentativo e un timeout per interrompere tutti i tentativi.

Secondo il libro “Designing Data-Intensive Applications” di Martin Kleppmann, il 50% dei guasti nei sistemi distribuiti sono temporanei e vengono risolti con un tentativo. Questo rende la Retry Policy uno dei modi più efficaci ed economici per migliorare la tolleranza ai guasti di un'applicazione mobile senza modificare l'architettura del server.

Quali errori dovrebbero essere ripetuti

Gli errori temporanei (ripetibili) sono l'unico tipo di guasto a cui la Retry Policy dovrebbe rispondere. Questi includono timeout di connessione (SocketTimeoutException), indisponibilità temporanea del server (HTTP 503, 502) ed errori DNS. Gli errori permanenti — HTTP 400, 401, 403, 404 — non devono essere ripetuti, poiché indicano un problema nella richiesta, non nella rete o nel server.

Secondo l'AWS Architecture Blog, la classificazione corretta degli errori in ripetibili e non ripetibili è la decisione più importante nella progettazione di una Retry Policy. Ripetere una richiesta non idempotente con HTTP 401 può portare al blocco dell'account, e ripetere HTTP 400 può creare dati duplicati. Configurare sempre esplicitamente l'elenco dei codici da ripetere.

Strategie di base di ripetizione

Intervallo fisso — la strategia più semplice: ogni tentativo viene eseguito dopo lo stesso intervallo di tempo. Ad esempio, con un ritardo di 2 secondi, l'applicazione ripete la richiesta dopo 2, 2, 2 secondi. L'intervallo fisso è semplice da implementare e prevedibile, ma crea un carico uniforme sul server in caso di guasti di massa.

Intervallo incrementale — il ritardo aumenta linearmente ad ogni tentativo: primo tentativo dopo 1 secondo, secondo dopo 2, terzo dopo 3 e così via. Questa strategia dà al server più tempo per riprendersi in caso di guasti ripetuti, ma è ancora prevedibile per molti client che falliscono simultaneamente.

StrategiaFormula del ritardoTempo cumulativo (3 tentativi)Applicazione
Fissodelay = D3 × DScenari semplici, timeout locali
Incrementaledelay = N × D6 × DRiduzione graduale del carico
Esponenzialedelay = D × 2^N7 × DGuasti di massa, servizi cloud
Esponenziale + Jitterdelay = random(0, D × 2^N)variabileCarico elevato, microservizi

La scelta della strategia dipende dalla natura dell'applicazione. Per le attività in background di sincronizzazione dei dati sui dispositivi mobili, la strategia esponenziale con jitter è ottimale — offre la più alta probabilità di successo con il minimo carico sul server e sul dispositivo dell'utente.

Exponential Backoff e Jitter

Exponential backoff — una strategia in cui il ritardo tra i tentativi raddoppia ad ogni tentativo. Se il ritardo iniziale è di 1 secondo, la sequenza dei ritardi sarà 1, 2, 4, 8, 16 secondi. Questo dà al server un tempo di recupero esponenzialmente crescente.

Jitter — una deviazione casuale del ritardo che impedisce richieste di tentativo sincrone da più client (problema del thundering herd). Senza jitter, mille client con la stessa Retry Policy ripeterebbero le richieste contemporaneamente, creando un carico di picco sul server. Lo jitter distribuisce i tentativi nel tempo.

Implementazione in Kotlin con coroutine

Le coroutine di Kotlin consentono di implementare l'exponential backoff con jitter senza bloccare il thread principale. La funzione retry di kotlinx-coroutines accetta una condizione di ripetizione e un blocco con il corpo della richiesta, gestendo automaticamente ritardi e numero di tentativi.

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

La funzione executeWithRetry accetta una lambda con una chiamata di rete e la esegue con exponential backoff e jitter. Se l'errore non è ripetibile o il numero massimo di tentativi viene superato, la funzione restituisce un errore. Il ritardo viene moltiplicato per un fattore casuale da 0.5 a 1.5 per una distribuzione uniforme dei tentativi.

Circuit Breaker e interruzione dei tentativi

Circuit Breaker — un pattern di progettazione che impedisce richieste di tentativo infinite durante l'indisponibilità prolungata del servizio. Quando il numero di errori supera una soglia, il Circuit Breaker passa allo stato OPEN e restituisce immediatamente un errore senza eseguire la richiesta, dando al server tempo per riprendersi.

Nelle applicazioni mobili, il Circuit Breaker è particolarmente utile quando un'API non è disponibile a causa di manutenzione programmata o guasti di rete dell'operatore. Senza di esso, l'applicazione consumerebbe batteria e traffico in tentativi infiniti, degradando l'esperienza utente e riducendo l'autonomia del dispositivo.

Diagramma degli stati del Circuit Breaker

Il Circuit Breaker ha tre stati: CLOSED (funzionamento normale, le richieste vengono eseguite), OPEN (guasto, le richieste vengono bloccate) e HALF_OPEN (richiesta di prova per verificare il recupero). Dopo un timeout specificato nello stato OPEN, l'interruttore passa a HALF_OPEN ed esegue una richiesta — in caso di successo torna a CLOSED, in caso di fallimento a 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
        }
    }
}

L'implementazione del Circuit Breaker in Kotlin contiene un contatore di errori e un timer di recupero. Il metodo protect verifica lo stato corrente prima di eseguire la richiesta incapsulata e aggiorna il contatore degli errori in caso di guasto. Dopo aver raggiunto failureThreshold, tutte le richieste vengono immediatamente rifiutate fino alla scadenza di timeoutMs.

Retry Policy nelle applicazioni mobili

Le reti mobili hanno caratteristiche che rendono la Retry Policy particolarmente importante. Il passaggio tra Wi-Fi e dati mobili, la perdita di segnale in metropolitane e tunnel, i blocchi temporanei a livello di operatore — tutti questi scenari portano a guasti delle richieste che possono essere gestiti con successo dai tentativi.

Su Android, la libreria Retrofit e OkHttp forniscono un meccanismo di ripetizione integrato tramite Interceptor. Su iOS, il compito viene risolto tramite URLSessionConfiguration e delega personalizzata. Per lo sviluppo multipiattaforma, Ktor (KMP) include il supporto integrato per la ripetizione con strategie configurabili.

Implementazione su iOS con Combine

Combine — il framework di Apple per la programmazione reattiva. L'operatore retry in Combine ripete il publisher un numero specificato di volte in caso di errore, ma non consente di configurare il ritardo tra i tentativi. Per una Retry Policy completa, viene utilizzata una combinazione personalizzata di catch e flatMap con ritardo.

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

L'estensione retryWithBackoff per Publisher in Combine implementa l'exponential backoff tramite chiamate ricorsive con contatore decrescente e ritardo raddoppiato. L'operatore delay crea una pausa tra i tentativi, mentre catch intercetta l'errore e decide se riprovare o restituire un fallimento.

Errori comuni nella Retry Policy

Il primo errore — ripetere le richieste senza verificare l'idempotenza. Se il server ha creato una risorsa ma non ha restituito conferma a causa di un guasto di rete, un tentativo creerà un duplicato. Per le richieste POST, utilizzare sempre una chiave di idempotenza (Idempotency-Key) nell'intestazione o limitare i tentativi solo a GET, PUT e DELETE.

Il secondo errore — ripetere all'infinito. Impostare sempre un numero massimo di tentativi (3–5 per applicazioni mobili) e un timeout totale per tutti i tentativi. I tentativi infiniti scaricano la batteria e creano un carico parassita sul server, specialmente durante migrazioni di database o modifiche API.

Il terzo errore — ignorare il contesto dell'applicazione. Se l'utente ha chiuso l'applicazione o è passato in background, le Retry Policy attive devono essere annullate correttamente. Utilizzare coroutine con SupervisorScope o Combine con il ciclo di vita dell'interfaccia utente per l'annullamento automatico dei tentativi alla chiusura dello schermo.

Il quarto errore — non registrare i tentativi. Senza registrazione, non saprai quante richieste sono state ripetute, quali errori si sono verificati e quanto è efficace la tua Retry Policy. Aggiungi metriche: numero di tentativi, successo dopo il tentativo, distribuzione dei ritardi. Questi dati aiuteranno a ottimizzare i parametri della strategia per la tua applicazione specifica.

Domande frequenti

Quante volte ripetere una richiesta in un'applicazione mobile?

Il numero ottimale di tentativi è di 3–5 per la maggior parte degli scenari. Per la sincronizzazione in background, 5–7 tentativi sono accettabili; per richieste interattive (ad esempio, invio di moduli), non più di 3. Un numero maggiore di tentativi non aumenta la probabilità di successo ma consuma la batteria e il traffico dati dell'utente.

Cos'è l'exponential backoff in parole semplici?

Exponential backoff — il raddoppio del ritardo tra i tentativi: 1 secondo, 2, 4, 8, 16 e così via. Se il server è sovraccarico, la breve pausa tra i primi tentativi gli consente di rispondere rapidamente, mentre la pausa crescente ad ogni tentativo successivo dà al server più tempo per riprendersi.

Quali codici di stato HTTP dovrebbero essere ripetuti?

Ripetere solo errori temporanei: 408 (Request Timeout), 429 (Too Many Requests), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout). Gli errori 4xx (eccetto 408 e 429) indicano problemi del cliente — ripeterli non ha senso e può essere pericoloso per i dati dell'utente.

Qual è la differenza tra Retry Policy e Circuit Breaker?

Retry Policy gestisce la ripetizione di una singola richiesta in caso di guasto. Il Circuit Breaker gestisce lo stato della connessione con un servizio: quando gli errori si accumulano, apre il circuito (OPEN) e blocca le nuove richieste. Il tentativo funziona a livello di singola chiamata, il Circuit Breaker a livello di integrazione del servizio.

Come testare la Retry Policy sui dispositivi mobili?

Per testare la Retry Policy, utilizzare NetworkInterceptor (OkHttp) su Android e URLProtocol (URLSession) su iOS per simulare guasti di rete. Impostare parametri: frequenza degli errori, durata dell'indisponibilità e codici di risposta. I test unitari con MockWebServer (OkHttp) o OHHTTPStubs (iOS) verificano la logica di ripetizione senza rete reale.

Riepilogo

  • Retry Policy — una strategia di ripetizione automatica delle richieste di rete durante guasti temporanei con parametri configurabili di ritardo e numero di tentativi.
  • Exponential backoff con jitter — la strategia di base per le applicazioni mobili, riduce il carico del server durante guasti di massa e previene l'effetto thundering herd.
  • Idempotenza — una condizione obbligatoria per ripetere in sicurezza richieste non GET: senza di essa, un tentativo crea dati duplicati o effetti collaterali indesiderati.
  • Circuit Breaker completa la Retry Policy prevenendo tentativi infiniti durante l'indisponibilità prolungata del servizio e conservando le risorse del dispositivo.
  • Classificazione degli errori in ripetibili (503, 502, timeout) e non ripetibili (400, 401, 403) è di importanza critica per il corretto funzionamento della politica di ripetizione.
  • Massimo 3–5 tentativi in scenari interattivi e fino a 7 per la sincronizzazione in background è il valore ottimale per le applicazioni mobili secondo Google Developer Relations.
  • Raccomandazione — implementare Retry Policy con exponential backoff, Circuit Breaker e registrazione per tutte le richieste di rete nelle applicazioni mobili.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche