تصميم معماريات Coroutines مرنة
اجمع بين supervisorScope ومنطق إعادة المحاولة والمعالجات لتحقيق تزامن مناسب لبيئات الإنتاج.
تصميم معماريات Coroutines مرنة درس مجاني في Kotlin Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Kotlin Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Kotlin Academy 4 دروس في المجموع.
ما الذي يجعل coroutines قادرة على الصمود؟
تتعامل بنى coroutines القادرة على الصمود مع حالات الفشل بسلاسة؛ فتعزل حالات الفشل، وتعيد محاولة الأخطاء المؤقتة، وتلغي العمل المتقادم، وتنظف الموارد بشكل حتمي.
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") }نمط النطاق على مستوى التطبيق
أنشئ CoroutineScope واحدًا على مستوى التطبيق باستخدام SupervisorJob + handler. ومرّره إلى الخدمات لتشارك جميعها دورة الحياة نفسها.
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
}
}
}مهلة لكل استدعاء خارجي
غلّف دائمًا عمليات الإدخال والإخراج الخارجية باستخدام withTimeout أو withTimeoutOrNull. ولا تسمح أبدًا لاتصال متعطل بمنع 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")
}نطاق منظم لكل ميزة
أنشئ CoroutineScope منفصلًا لكل ميزة أو شاشة، مع SupervisorJob خاص به. وألغِه عند تدمير الميزة.
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()
استخدم use { } مع موارد AutoCloseable. وادمجه مع إلغاء coroutine لضمان إغلاق الموارد حتى عند إلغاء coroutine.
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()
}الإيقاف السلس
نفّذ إيقافًا سلسًا: أوقف قبول العمل الجديد، وانتظر انتهاء coroutines قيد التنفيذ، ثم ألغِ النطاق.
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
وسم coroutines باستخدام 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 مطلقًا في بيئة الإنتاج. فهو ينشئ coroutines بلا أب، ولا إدارة لدورة الحياة، ولا إلغاء منظم. استخدم بدائل مرتبطة بنطاقات بدلًا منه.
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() }اختبار القدرة على الصمود
استخدم runTest مع TestCoroutineScheduler لمحاكاة حالات الفشل والمهلات وإعادة المحاولة في اختبارات الوحدات دون فترات تأخير فعلية.
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تحقق سريع
أي مجموعة تشكل أساس نطاق coroutine طويل العمر وقادر على الصمود؟
مراجعة
تجمع البنى القادرة على الصمود بين SupervisorJob للعزل، وretryWithBackoff للأخطاء المؤقتة، وwithTimeout لعمليات الإدخال والإخراج المتوقفة، وfinally/NonCancellable للتنظيف، وCoroutineExceptionHandler للرصد.
الأسئلة الشائعة
هل درس «تصميم معماريات Coroutines مرنة» مجاني؟
نعم — نص درس «تصميم معماريات Coroutines مرنة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Kotlin Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Kotlin Academy 4 دروس في المجموع.
ماذا ستتعلم في «تصميم معماريات Coroutines مرنة»؟
اجمع بين supervisorScope ومنطق إعادة المحاولة والمعالجات لتحقيق تزامن مناسب لبيئات الإنتاج. تتمرن على Kotlin Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Kotlin Academy؟
لا تُشترط خبرة سابقة. Kotlin Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «تصميم معماريات Coroutines مرنة»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Kotlin Academy هذا؟
نعم. كل درس في Kotlin Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- SupervisorJob مقابل Job: عزل حالات الفشل
- CoroutineExceptionHandler: المعالج العام للاستثناءات غير الملتقطة
- انتشار الاستثناءات مع async/await
- تصميم معماريات Coroutines مرنة