Kotlin Academy · Lektion

Design af robuste coroutine-arkitekturer

Kombinér supervisorScope, retry-logik og handlers til concurrency på produktionsniveau.

Lektion 4 af 413 trin

Design af robuste coroutine-arkitekturer er en gratis Kotlin Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Kotlin Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Kotlin Academy-kurset indeholder 4 lektioner i alt.

Hvad gør koroutiner robuste?

Robuste koroutinearkitekturer håndterer fejl på en god måde: de isolerer fejl, prøver midlertidige fejl igen, annullerer forældet arbejde og rydder deterministisk op i ressourcer.

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

Mønster for omfang på appniveau

Opret et enkelt CoroutineScope på appniveau med SupervisorJob + handler. Injicér det i tjenester, så de alle deler den samme livscyklus.

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

Nyt forsøg med eksponentiel ventetid

Pak netværkskald ind i en løkke med nye forsøg og eksponentiel ventetid for at håndtere midlertidige fejl på en god måde.

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

Mønster for kredsløbsafbryder

En kredsløbsafbryder stopper kald til en tjeneste, der fejler, efter N fejl og åbner igen efter en afkølingsperiode — så kaskadefejl forhindres.

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

Timeout for alle eksterne kald

Pak altid ekstern I/O ind i withTimeout eller withTimeoutOrNull. Lad aldrig en hængende forbindelse blokere en koroutine på ubestemt tid.

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

Struktureret omfang pr. funktion

Opret et separat CoroutineScope for hver funktion eller skærm med sit eget SupervisorJob. Annullér det, når funktionen destrueres.

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
}

Sikring af ressourcer med use()

Brug use { } til AutoCloseable-ressourcer. Kombinér det med annullering af koroutinen: Ressourcer lukkes, også når koroutinen annulleres.

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

Kontrolleret nedlukning

Implementér en kontrolleret nedlukning: Stop med at acceptere nyt arbejde, vent på, at igangværende koroutiner afsluttes, og annullér derefter omfanget.

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

Observerbarhed: CoroutineName

Mærk koroutiner med CoroutineName til fejlfinding. Navnene vises i stakspor og kan bruges i undtagelseshåndteringer til struktureret logning.

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

Undgå GlobalScope

Brug aldrig GlobalScope i produktion. Det opretter koroutiner uden forælder, uden livscyklusstyring og uden struktureret annullering. Brug i stedet alternativer med et afgrænset omfang.

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

Test af robusthed

Brug runTest sammen med TestCoroutineScheduler til at simulere fejl, timeouts og nye forsøg i enhedstest uden reelle forsinkelser.

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 delays

Hurtigt tjek

Hvilken kombination danner grundlaget for et robust, langvarigt koroutineomfang?

Opsamling

Robuste arkitekturer kombinerer SupervisorJob til isolering, retryWithBackoff til midlertidige fejl, withTimeout til fastlåst I/O, finally/NonCancellable til oprydning og CoroutineExceptionHandler til observerbarhed.

Gratis at komme i gang

Lær Kotlin med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
51
Lektioner
203

Ofte stillede spørgsmål

Er lektionen “Design af robuste coroutine-arkitekturer” gratis?

Ja — alle 3 lektioner i læringssporet Kotlin Academy, inklusive “Design af robuste coroutine-arkitekturer”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Kotlin Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Design af robuste coroutine-arkitekturer”?

Kombinér supervisorScope, retry-logik og handlers til concurrency på produktionsniveau. Du øver dig i Kotlin Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Kotlin Academy?

Der kræves ingen tidligere erfaring. Kotlin Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.

Hvor lang tid tager lektionen “Design af robuste coroutine-arkitekturer”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Kotlin Academy-lektion?

Ja. Alle Kotlin Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. SupervisorJob vs Job: Isolering af fejl
  2. CoroutineExceptionHandler: Global handler til ubehandlede exceptions
  3. Exception-formidling med async/await
  4. Design af robuste coroutine-arkitekturer
← Tilbage til Kotlin Academy