Progettare architetture resilienti per le coroutine
Combini supervisorScope, logica di retry e handler per una concorrenza adatta alla produzione.
Progettare architetture resilienti per le coroutine è una lezione Kotlin Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Kotlin Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Kotlin Academy include 4 lezioni in totale.
Che cosa rende resilienti le coroutine?
Le architetture resilienti basate sulle coroutine gestiscono gli errori in modo appropriato: isolano gli errori, ritentano le operazioni che falliscono temporaneamente, annullano il lavoro obsoleto e puliscono le risorse in modo deterministico.
import kotlinx.coroutines.*
// Resilience pillars:
// 1. Failure isolation (SupervisorJob)
// 2. Retry with backoff (retry operator / loop)
// 3. Timeout guards (withTimeout)
// 4. Clean teardown (finally + NonCancellable)
// 5. Observability (CoroutineExceptionHandler)
fun main() = runBlocking { println("Design for failure from the start") }Modello di scope a livello di applicazione
Crei un unico CoroutineScope a livello di applicazione con SupervisorJob + handler. Lo inietti nei servizi, così condividono tutti lo stesso ciclo di vita.
import kotlinx.coroutines.*
object AppCoroutineScope {
private val handler = CoroutineExceptionHandler { _, e ->
println("[AppScope] Uncaught: ${e.message}")
}
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default + handler)
fun cancel() = scope.cancel()
}Nuovi tentativi con backoff esponenziale
Racchiuda le chiamate di rete in un ciclo di nuovi tentativi con backoff esponenziale per gestire in modo appropriato gli errori temporanei.
import kotlinx.coroutines.*
suspend fun <T> retryWithBackoff(
times: Int = 3,
initialDelay: Long = 100,
block: suspend () -> T
): T {
var delay = initialDelay
repeat(times - 1) { attempt ->
try { return block() }
catch (e: Exception) {
if (e is CancellationException) throw e
println("Attempt ${attempt+1} failed, retrying in ${delay}ms")
delay(delay)
delay *= 2
}
}
return block()
}
fun main() = runBlocking {
var n = 0
val result = retryWithBackoff {
if (n++ < 2) throw RuntimeException("transient")
"success"
}
println(result)
}Modello Circuit Breaker
Un circuit breaker interrompe le chiamate a un servizio che presenta errori dopo N errori e le riattiva dopo un periodo di attesa, prevenendo gli errori a cascata.
import kotlinx.coroutines.*
class CircuitBreaker(val maxFailures: Int, val cooldownMs: Long) {
private var failures = 0
private var openUntil = 0L
suspend fun <T> call(block: suspend () -> T): T {
if (System.currentTimeMillis() < openUntil) throw RuntimeException("Circuit open")
return try {
val result = block()
failures = 0
result
} catch (e: Exception) {
if (e is CancellationException) throw e
if (++failures >= maxFailures) openUntil = System.currentTimeMillis() + cooldownMs
throw e
}
}
}Imposti un timeout per ogni chiamata esterna
Racchiuda sempre l'I/O esterno in withTimeout o withTimeoutOrNull. Non permetta mai che una connessione bloccata impedisca indefinitamente l'avanzamento di una coroutine.
import kotlinx.coroutines.*
suspend fun fetchWithTimeout(url: String): String? = withTimeoutOrNull(3000) {
// ktor: client.get(url).body()
delay(100) // simulate
"response from $url"
}
fun main() = runBlocking {
val result = fetchWithTimeout("https://api.example.com")
println(result ?: "Timed out")
}Scope strutturato per ogni funzionalità
Crei un CoroutineScope separato per ogni funzionalità o schermata, con il proprio SupervisorJob. Lo annulli quando la funzionalità viene eliminata.
import kotlinx.coroutines.*
class FeatureController {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
fun start() {
scope.launch { /* load data */ }
scope.launch { /* subscribe to updates */ }
}
fun destroy() = scope.cancel() // cancels all feature coroutines
}Sicurezza delle risorse con use()
Utilizzi use { } per le risorse AutoCloseable. Lo combini con l'annullamento delle coroutine: le risorse vengono chiuse anche quando la coroutine viene annullata.
import kotlinx.coroutines.*
class Connection : AutoCloseable {
override fun close() = println("Connection closed")
suspend fun fetch(): String { delay(100); return "data" }
}
fun main() = runBlocking {
val job = launch {
Connection().use { conn ->
println(conn.fetch())
} // close() called even on cancellation
}
delay(50)
job.cancelAndJoin()
}Arresto controllato
Implementi un arresto controllato: smetta di accettare nuovo lavoro, attenda il completamento delle coroutine in corso, quindi annulli lo scope.
import kotlinx.coroutines.*
class WorkQueue {
private val scope = CoroutineScope(SupervisorJob())
private val jobs = mutableListOf<Job>()
fun submit(block: suspend () -> Unit) {
jobs += scope.launch { block() }
}
suspend fun shutdown() {
jobs.forEach { it.join() } // wait for all
scope.cancel() // then cancel scope
}
}
fun main() = runBlocking {
val q = WorkQueue()
repeat(3) { i -> q.submit { delay(50); println("Task $i done") } }
q.shutdown()
println("Queue shut down cleanly")
}Osservabilità: CoroutineName
Assegni un tag alle coroutine con CoroutineName per semplificare il debug. I nomi compaiono nelle tracce dello stack e possono essere usati nei gestori delle eccezioni per la registrazione strutturata.
import kotlinx.coroutines.*
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { ctx, e ->
println("[${ctx[CoroutineName]?.name}] Failed: ${e.message}")
}
CoroutineScope(SupervisorJob() + handler).apply {
launch(CoroutineName("UserLoader")) { throw RuntimeException("DB error") }
launch(CoroutineName("Analytics")) { delay(100); println("Analytics ok") }
delay(200); cancel()
}
}Eviti GlobalScope
Non utilizzi mai GlobalScope in produzione. Crea coroutine senza un padre, senza gestione del ciclo di vita e senza annullamento strutturato. Utilizzi invece alternative basate sugli scope.
import kotlinx.coroutines.*
// BAD — GlobalScope leaks coroutines:
// GlobalScope.launch { delay(Long.MAX_VALUE) }
// GOOD — scoped, cancellable:
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
scope.launch { delay(100); println("Scoped") }
fun main() = runBlocking { delay(200); scope.cancel() }Testare la resilienza
Utilizzi runTest con TestCoroutineScheduler per simulare errori, timeout e nuovi tentativi nei test unitari senza ritardi reali.
import kotlinx.coroutines.*
import kotlinx.coroutines.test.*
fun testRetry() = runTest {
var attempts = 0
val result = retryWithBackoff(3, 100) {
if (attempts++ < 2) throw RuntimeException("fail")
"ok"
}
println(result) // "ok"
}
// In test, virtual time advances instantly through delaysVerifica rapida
Quale combinazione costituisce la base di uno scope di coroutine resiliente e di lunga durata?
Riepilogo
Le architetture resilienti combinano SupervisorJob per l'isolamento, retryWithBackoff per gli errori temporanei, withTimeout per l'I/O bloccato, finally/NonCancellable per la pulizia e CoroutineExceptionHandler per l'osservabilità.
Impara Kotlin con un tutor IA — gratis
Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.
- Corsi
- 51
- Lezioni
- 203
Domande Frequenti
La lezione «Progettare architetture resilienti per le coroutine» è gratuita?
Sì — il testo completo di «Progettare architetture resilienti per le coroutine» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Kotlin Academy, passa a CoddyKit PRO. Il corso Kotlin Academy include 4 lezioni in totale.
Cosa imparerò in «Progettare architetture resilienti per le coroutine»?
Combini supervisorScope, logica di retry e handler per una concorrenza adatta alla produzione. Eserciti Kotlin Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Kotlin Academy?
Non è richiesta alcuna esperienza precedente. Kotlin Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Progettare architetture resilienti per le coroutine»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Kotlin Academy?
Sì. Ogni lezione Kotlin Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- SupervisorJob e Job: isolamento degli errori
- CoroutineExceptionHandler: gestore globale delle eccezioni non catturate
- Propagazione delle eccezioni con async/await
- Progettare architetture resilienti per le coroutine