الإشراف وتحمل الأعطال
استكشف استراتيجيات الإشراف في Akka لإنشاء أنظمة مرنة قادرة على التعافي من الأعطال بسلاسة
الإشراف وتحمل الأعطال درس مجاني في Scala for Backend Engineering & Functional Programming على CoddyKit. هذا هو الدرس 3 من أصل 3. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Scala for Backend Engineering & Functional Programming، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Scala for Backend Engineering & Functional Programming 3 دروس في المجموع.
بناء أنظمة مرنة
قد تسوء الأمور في التطبيقات المتزامنة. فقد يتعطل أحد الممثلين، أو تفشل مكالمة شبكة، أو يتعذر الوصول إلى قاعدة بيانات.
تحمّل الأخطاء هو قدرة النظام على مواصلة عمله بصورة صحيحة حتى عند تعطل بعض أجزائه. يوفر Akka آليات قوية لتحقيق ذلك.
الإشراف بين الآباء والأبناء
يُنظَّم ممثلو Akka في تسلسل هرمي يشبه إلى حد كبير شجرة العائلة. عندما ينشئ ممثلٌ ممثلًا آخر، يصبح أبًا له.
- يتحمل الآباء مسؤولية الإشراف على أبنائهم.
- إذا فشل أحد الممثلين الأبناء، يتلقى أبوه إشعارًا بذلك.
- ثم يقرر الأب كيفية التعامل مع فشل الابن. ويُسمى ذلك استراتيجية الإشراف.
اتخاذ قرار بشأن الفشل
عند فشل أحد الممثلين، يمكن لأبيه إصدار أحد التوجيهات الأربعة:
Resume: يواصل الممثل معالجة الرسائل متجاهلًا الفشل.Restart: يُوقَف الممثل ويُعاد إنشاؤه، ثم يستأنف المعالجة. وتُعاد تهيئة حالته الداخلية.Stop: يُنهى الممثل نهائيًا.Escalate: يُرفع الفشل إلى مشرف الأب، أي الجد.
استراتيجية OneForOne
تُعد OneForOneStrategy استراتيجية إشراف شائعة. وتعني أن التوجيه ينطبق فقط على الممثل الابن الذي فشل.
فعلى سبيل المثال، إذا تعطل أحد الممثلين الأبناء، فقد يقرر الأب إعادة تشغيل ذلك الابن وحده، مع إبقاء الأبناء الآخرين دون تأثر.
تطبيق OneForOne عمليًا
لنلقِ نظرة على OneForOneStrategy. سننشئ أبًا يعيد تشغيل أحد أبنائه عند حدوث ArithmeticException، لكنه يوقفه عند حدوث استثناءات أخرى.
import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
object OneForOneExample extends App {
class Child extends Actor {
override def preStart(): Unit = {
println(s"Child ${self.path.name}: Started!")
}
override def postStop(): Unit = {
println(s"Child ${self.path.name}: Stopped!")
}
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
println(s"Child ${self.path.name}: Pre-restart due to ${reason.getMessage}")
}
override def postRestart(reason: Throwable): Unit = {
println(s"Child ${self.path.name}: Post-restart!")
}
def receive = {
case "divideByZero" => throw new ArithmeticException("Oops, dividing by zero!")
case "nullPointer" => throw new NullPointerException("Something is null!")
case msg => println(s"Child ${self.path.name}: Received $msg")
}
}
class Parent extends Actor {
override val supervisorStrategy: SupervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1.minute) {
case _: ArithmeticException => Restart
case _: NullPointerException => Stop
case _ => Escalate
}
val child = context.actorOf(Props[Child], "myChild")
def receive = {
case msg => child ! msg
}
}
val system = ActorSystem("OneForOneSystem")
val parent = system.actorOf(Props[Parent], "myParent")
parent ! "Hello"
parent ! "divideByZero" // Should cause a restart
Thread.sleep(100)
parent ! "Hello again"
Thread.sleep(1000) // Give time for restart to complete
parent ! "nullPointer" // Should cause a stop
Thread.sleep(100)
parent ! "Hello after null" // This message will not be processed by child
Thread.sleep(2000)
system.terminate()
}ملاحظة سلوك OneForOne
هل شغّلتم الرمز السابق؟ إليكم ما حدث:
- عند إرسال
"divideByZero"، ألقى الابن استثناءArithmeticException. طبّق الأب التوجيهRestart. وقد رأيتم سجلي "Pre-restart" و"Post-restart". - عالج الابن بعد ذلك الرسالة "Hello again".
- عند إرسال
"nullPointer"، ألقى الابن استثناءNullPointerException. طبّق الأب التوجيهStop، فتم إنهاء الابن. - أُرسلت الرسالة "Hello after null"، لكن الابن كان قد أُوقف بالفعل ولم يعد قادرًا على معالجتها.
استراتيجية AllForOne
قد يعني الفشل أحيانًا في أحد الأبناء أن جميع الأبناء الأشقاء قد يتأثرون أيضًا أو يصبحون غير قادرين على العمل بصورة صحيحة.
تطبّق AllForOneStrategy التوجيه نفسه على جميع أبناء المشرف، وليس على الابن الذي فشل وحده. فإذا فشل أحد الأبناء، يتأثر جميع الأبناء بالتوجيه المختار.
تطبيق AllForOne عمليًا
لنعدّل الأب ليستخدم AllForOneStrategy. لاحظوا كيف يؤثر فشل أحد الأبناء في أخيه.
import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
object AllForOneExample extends App {
class Child extends Actor {
override def preStart(): Unit = {
println(s"Child ${self.path.name}: Started!")
}
override def postStop(): Unit = {
println(s"Child ${self.path.name}: Stopped!")
}
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
println(s"Child ${self.path.name}: Pre-restart due to ${reason.getMessage}")
}
override def postRestart(reason: Throwable): Unit = {
println(s"Child ${self.path.name}: Post-restart!")
}
def receive = {
case "fail" => throw new RuntimeException("Child failed!")
case msg => println(s"Child ${self.path.name}: Received $msg")
}
}
class Parent extends Actor {
override val supervisorStrategy: SupervisorStrategy =
AllForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1.minute) {
case _: RuntimeException => Restart
case _ => Escalate
}
val childA = context.actorOf(Props[Child], "childA")
val childB = context.actorOf(Props[Child], "childB")
def receive = {
case "failA" => childA ! "fail"
case "msgB" => childB ! "Hello from B"
case msg => println(s"Parent received: $msg")
}
}
val system = ActorSystem("AllForOneSystem")
val parent = system.actorOf(Props[Parent], "myParent")
parent ! "msgB" // Child B receives a message
Thread.sleep(100)
parent ! "failA" // Child A fails, causing all children to restart
Thread.sleep(1000)
parent ! "msgB" // Child B (restarted) receives another message
Thread.sleep(2000)
system.terminate()
}تخصيص أدوات اتخاذ القرار
يمكنكم إنشاء منطق إشراف مخصص بدرجة كبيرة باستخدام دالة اتخاذ القرار. تأخذ هذه الدالة كائن Throwable (الاستثناء) وتعيد كائن Directive.
يتيح لكم ذلك تنفيذ قواعد معقدة تستند إلى نوع الاستثناء، أو الرسالة التي تسببت فيه، أو حتى حالة المشرف.
الإشراف المتقدم
إليكم كيفية تعريف أداة اتخاذ قرار مخصصة ضمن استراتيجية المشرف للتحكم الدقيق.
import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
object CustomDeciderExample extends App {
class Worker extends Actor {
def receive = {
case "criticalError" => throw new IllegalStateException("Critical state!")
case "minorError" => throw new IllegalArgumentException("Bad input!")
case msg => println(s"Worker: Received $msg")
}
}
class Supervisor extends Actor {
override val supervisorStrategy: SupervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 3, withinTimeRange = 10.seconds) {
case _: IllegalArgumentException => Resume // Minor error, just resume
case _: IllegalStateException => Restart // Critical, restart
case _: Exception => Stop // Unknown, stop
case _ => Escalate // Others, escalate
}
val worker = context.actorOf(Props[Worker], "myWorker")
def receive = {
case msg => worker ! msg
}
}
val system = ActorSystem("CustomDeciderSystem")
val supervisor = system.actorOf(Props[Supervisor], "mySupervisor")
supervisor ! "Hello"
supervisor ! "minorError" // Should Resume
Thread.sleep(100)
supervisor ! "Hello again" // Worker should still be active
Thread.sleep(500)
supervisor ! "criticalError" // Should Restart
Thread.sleep(100)
supervisor ! "After critical" // Worker should be restarted
Thread.sleep(1500)
system.terminate()
}اختبروا معرفتكم
تأملوا ممثلًا أبًا يشرف على ممثلين ابنين، childA وchildB. يستخدم الأب AllForOneStrategy مع التوجيه Restart لجميع الاستثناءات.
ماذا سيحدث إذا ألقى childA استثناءً؟
مراجعة: تحمّل الأخطاء
أحسنتم! لقد تعلمتم في هذا الدرس آليات الإشراف القوية في Akka:
- التسلسل الهرمي للإشراف: يشرف الآباء على الأبناء.
- التوجيهات:
ResumeوRestartوStopوEscalate. OneForOneStrategy: تطبّق التوجيهات على الابن الذي فشل فقط.AllForOneStrategy: تطبّق التوجيهات على جميع الأبناء.- أدوات اتخاذ القرار المخصصة: للتحكم الدقيق في التعامل مع حالات الفشل.
تُعد هذه الأدوات ضرورية لبناء تطبيقات متزامنة قوية ومرنة باستخدام ممثلي Akka!
تعلم Scala مع معلم ذكاء اصطناعي — مجانًا
اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.
- الدورات
- 39
- الدروس
- 143
الأسئلة الشائعة
هل درس «الإشراف وتحمل الأعطال» مجاني؟
نعم — نص درس «الإشراف وتحمل الأعطال» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Scala for Backend Engineering & Functional Programming، انتقل إلى CoddyKit PRO. تتضمن دورة Scala for Backend Engineering & Functional Programming 3 دروس في المجموع.
ماذا ستتعلم في «الإشراف وتحمل الأعطال»؟
استكشف استراتيجيات الإشراف في Akka لإنشاء أنظمة مرنة قادرة على التعافي من الأعطال بسلاسة تتمرن على Scala for Backend Engineering & Functional Programming مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Scala for Backend Engineering & Functional Programming؟
لا تُشترط خبرة سابقة. Scala for Backend Engineering & Functional Programming على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 3.
كم من الوقت يستغرق درس «الإشراف وتحمل الأعطال»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Scala for Backend Engineering & Functional Programming هذا؟
نعم. كل درس في Scala for Backend Engineering & Functional Programming يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- أساسيات نموذج Akka Actor
- تصميم أنظمة Actor
- الإشراف وتحمل الأعطال