0Pricing
Kotlin Academy · レッスン

堅牢なコルーチンアーキテクチャの設計

supervisorScope、リトライロジック、ハンドラーを組み合わせ、本番品質の並行処理を実現します。

「堅牢なコルーチンアーキテクチャの設計」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

コルーチンをレジリエントにするもの

レジリエントなコルーチンアーキテクチャは、失敗を適切に処理します。失敗を分離し、一時的なエラーを再試行し、古くなった処理をキャンセルし、リソースを確実にクリーンアップします。

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

アプリレベルのスコープパターン

SupervisorJob + handler を備えた単一のアプリレベル CoroutineScope を作成します。各サービスに注入することで、すべてのサービスで同じライフサイクルを共有できます。

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

指数バックオフによるリトライ

ネットワーク呼び出しを指数バックオフ付きのリトライループでラップし、一時的な失敗を適切に処理します。

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

サーキットブレーカーパターン

サーキットブレーカーは、N 回失敗したサービスへの呼び出しを停止し、クールダウン後に再開します。これにより、連鎖的な障害を防ぎます。

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

すべての外部呼び出しにタイムアウトを設定する

外部 I/O は必ず withTimeout または withTimeoutOrNull でラップします。ハングした接続によってコルーチンが無期限にブロックされることがないようにしてください。

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

機能ごとの構造化スコープ

機能または画面ごとに、独自の SupervisorJob を持つ別個の CoroutineScope を作成します。機能が破棄されたときにキャンセルしてください。

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
}

use() によるリソースの安全性

AutoCloseable リソースには use { } を使用します。コルーチンのキャンセルと組み合わせると、コルーチンがキャンセルされた場合でもリソースが閉じられます。

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

グレースフルシャットダウン

新しい処理の受け付けを停止し、実行中のコルーチンが完了するまで待機してからスコープをキャンセルする、グレースフルシャットダウンを実装します。

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

可観測性:CoroutineName

デバッグのために CoroutineName でコルーチンに名前を付けます。名前はスタックトレースに表示され、例外ハンドラーで構造化ロギングに利用できます。

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

GlobalScope を避ける

本番環境では GlobalScope を決して使用しないでください。親、ライフサイクル管理、構造化されたキャンセルのないコルーチンを作成してしまいます。代わりに、スコープを持つ方法を使用してください。

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

レジリエンスのテスト

TestCoroutineScheduler と runTest を使用すると、実際の遅延なしにユニットテストで失敗、タイムアウト、リトライをシミュレートできます。

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

クイックチェック

長期間存続するレジリエントなコルーチンスコープの基盤となる組み合わせはどれですか。

振り返り

レジリエントなアーキテクチャでは、分離のための SupervisorJob、一時的なエラーのための retryWithBackoff、停止した I/O のための withTimeout、クリーンアップのための finally/NonCancellable、可観測性のための CoroutineExceptionHandler を組み合わせます。

よくある質問

「堅牢なコルーチンアーキテクチャの設計」レッスンは無料ですか?

はい。「堅牢なコルーチンアーキテクチャの設計」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「堅牢なコルーチンアーキテクチャの設計」で何を学びますか?

supervisorScope、リトライロジック、ハンドラーを組み合わせ、本番品質の並行処理を実現します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「堅牢なコルーチンアーキテクチャの設計」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. SupervisorJobとJob:失敗の分離
  2. CoroutineExceptionHandler:グローバルな未捕捉例外ハンドラー
  3. async/awaitによる例外の伝播
  4. 堅牢なコルーチンアーキテクチャの設計
← Kotlin Academyに戻る