أصبح التوافق مع الأخطاء والتسامح سهلاً: دروس في عكا مع أمثلة
نشرت: 2022-03-11التحدي
كتابة البرامج المتزامنة صعبة. إن الاضطرار إلى التعامل مع الخيوط والأقفال وظروف السباق وما إلى ذلك هو عرضة للخطأ بدرجة كبيرة ويمكن أن يؤدي إلى رمز يصعب قراءته واختباره وصيانته.
لذلك يفضل الكثير تجنب تعدد مؤشرات الترابط تمامًا. بدلاً من ذلك ، يستخدمون عمليات مترابطة فردية بشكل حصري ، معتمدين على الخدمات الخارجية (مثل قواعد البيانات وقوائم الانتظار وما إلى ذلك) للتعامل مع أي عمليات مطلوبة متزامنة أو غير متزامنة. في حين أن هذا النهج هو في بعض الحالات بديلاً مشروعًا ، إلا أن هناك العديد من السيناريوهات التي لا يكون فيها ببساطة خيارًا قابلاً للتطبيق. لا تتمتع العديد من أنظمة الوقت الفعلي - مثل التداول أو التطبيقات المصرفية أو الألعاب في الوقت الفعلي - برفاهية انتظار اكتمال عملية ذات سلسلة واحدة (فهي بحاجة إلى الإجابة الآن!). الأنظمة الأخرى كثيفة الحوسبة أو الموارد لدرجة أنها قد تستغرق وقتًا غير عادي (ساعات أو حتى أيام في بعض الحالات) للتشغيل دون إدخال التوازي في التعليمات البرمجية الخاصة بهم.
يتمثل أحد الأساليب الشائعة إلى حد ما في الخيط المفرد (المستخدم على نطاق واسع في عالم Node.js ، على سبيل المثال) في استخدام نموذج قائم على الحدث وغير محجوب. في حين أن هذا يساعد في الأداء من خلال تجنب تبديل السياق والأقفال والحظر ، إلا أنه لا يزال لا يعالج مشكلات استخدام معالجات متعددة في وقت واحد (يتطلب القيام بذلك بدء عمليات مستقلة متعددة والتنسيق بينها).
فهل هذا يعني أنه ليس لديك خيار سوى الذهاب في أعماق أحشاء الخيوط والأقفال وظروف العرق من أجل بناء تطبيق متزامن؟
بفضل إطار عكا ، الجواب هو لا. يقدم هذا البرنامج التعليمي أمثلة Akka ويستكشف الطرق التي تسهل وتبسط تنفيذ التطبيقات المتزامنة والموزعة.
ما هو إطار عكا؟
Akka عبارة عن مجموعة أدوات ووقت تشغيل لإنشاء تطبيقات متزامنة وموزعة للغاية ومتسامحة مع الأخطاء على JVM. تمت كتابة عكا بلغة سكالا ، مع توفير روابط لغوية لكل من سكالا وجافا.
نهج عكا للتعامل مع التزامن يعتمد على نموذج الممثل. في النظام القائم على الممثل ، كل شيء هو ممثل ، بنفس الطريقة التي يكون فيها كل شيء كائنًا في التصميم الموجه للكائنات. هناك اختلاف رئيسي - على الرغم من أنه وثيق الصلة بمناقشتنا - هو أن نموذج الممثل قد تم تصميمه وهندسته خصيصًا ليكون بمثابة نموذج متزامن في حين أن النموذج الموجه للكائنات ليس كذلك. وبشكل أكثر تحديدًا ، في نظام ممثل سكالا ، يتفاعل الممثلون ويشاركون المعلومات ، دون أي افتراض مسبق للتسلسل. الآلية التي يتشارك بها الفاعلون المعلومات مع بعضهم البعض ، ويقومون بمهمة بعضهم البعض ، هي تمرير الرسائل.
تنشئ Akka طبقة بين الممثلين والنظام الأساسي بحيث يحتاج الممثلون ببساطة إلى معالجة الرسائل. كل تعقيدات إنشاء وجدولة سلاسل الرسائل ، وتلقي الرسائل وإرسالها ، والتعامل مع ظروف السباق والمزامنة ، يتم تحويلها إلى إطار العمل للتعامل بشفافية.
تلتزم عكا بصرامة بالبيان التفاعلي. تهدف التطبيقات التفاعلية إلى استبدال التطبيقات التقليدية متعددة مؤشرات الترابط بهيكل يفي بواحد أو أكثر من المتطلبات التالية:
- الحدث مدفوعة. باستخدام الفاعلين ، يمكن للمرء كتابة التعليمات البرمجية التي تتعامل مع الطلبات بشكل غير متزامن وتستخدم عمليات غير معطلة حصريًا.
- القابلة للتطوير. في عكا ، يمكن إضافة العقد دون الحاجة إلى تعديل الكود ، وذلك بفضل كل من تمرير الرسائل وشفافية الموقع.
- مرن. سيواجه أي تطبيق أخطاء ويفشل في وقت ما. توفر عكا استراتيجيات "الإشراف" (تحمل الخطأ) لتسهيل نظام الشفاء الذاتي.
- متجاوب. تحتاج العديد من تطبيقات اليوم عالية الأداء والاستجابة السريعة إلى تقديم ملاحظات سريعة للمستخدم وبالتالي تحتاج إلى الاستجابة للأحداث في الوقت المناسب للغاية. تساعد إستراتيجية Akka غير المحظورة والقائمة على الرسائل في تحقيق ذلك.
من هو الممثل في عكا؟
الممثل هو في الأساس ليس أكثر من مجرد كائن يتلقى الرسائل ويتخذ إجراءات للتعامل معها. يتم فصلها عن مصدر الرسالة ومسؤوليتها الوحيدة هي التعرف بشكل صحيح على نوع الرسالة التي تلقتها واتخاذ الإجراءات وفقًا لذلك.
عند تلقي رسالة ، يجوز للممثل اتخاذ إجراء أو أكثر من الإجراءات التالية:
- تنفيذ بعض العمليات بنفسه (مثل إجراء العمليات الحسابية ، والبيانات المستمرة ، واستدعاء خدمة ويب خارجية ، وما إلى ذلك)
- قم بإعادة توجيه الرسالة أو الرسالة المشتقة إلى جهة فاعلة أخرى
- إنشاء ممثل جديد وإعادة توجيه الرسالة إليه
بدلاً من ذلك ، قد يختار الممثل تجاهل الرسالة تمامًا (على سبيل المثال ، قد يختار عدم اتخاذ أي إجراء) إذا رأى أنه من المناسب القيام بذلك.
لتنفيذ أحد الفاعلين ، من الضروري تمديد سمة akka.actor.Actor وتنفيذ طريقة الاستلام. يتم استدعاء طريقة تلقي الممثل (بواسطة Akka) عندما يتم إرسال رسالة إلى ذلك الممثل. يتكون تنفيذها النموذجي من مطابقة النمط ، كما هو موضح في مثال Akka التالي ، لتحديد نوع الرسالة والرد وفقًا لذلك:
import akka.actor.Actor import akka.actor.Props import akka.event.Logging class MyActor extends Actor { def receive = { case value: String => doSomething(value) case _ => println("received unknown message") } }
مطابقة الأنماط هي تقنية أنيقة نسبيًا للتعامل مع الرسائل ، والتي تميل إلى إنتاج رمز "أنظف" وأسهل في التنقل من تطبيق مشابه يعتمد على عمليات الاسترجاعات. ضع في اعتبارك ، على سبيل المثال ، تنفيذ طلب / استجابة بسيط لـ HTTP.
أولاً ، دعنا ننفذ هذا باستخدام نموذج قائم على رد الاتصال في JavaScript:
route(url, function(request){ var query = buildQuery(request); dbCall(query, function(dbResponse){ var wsRequest = buildWebServiceRequest(dbResponse); wsCall(wsRequest, function(wsResponse) { sendReply(wsResponse); }); }); });
الآن دعنا نقارن هذا بالتنفيذ المستند إلى مطابقة الأنماط:
msg match { case HttpRequest(request) => { val query = buildQuery(request) dbCall(query) } case DbResponse(dbResponse) => { var wsRequest = buildWebServiceRequest(dbResponse); wsCall(dbResponse) } case WsResponse(wsResponse) => sendReply(wsResponse) }
على الرغم من أن كود JavaScript المستند إلى رد الاتصال مضغوط ، فمن المؤكد أنه من الصعب قراءته والتنقل فيه. وبالمقارنة ، فإن الكود المستند إلى مطابقة الأنماط يجعل الأمر أكثر وضوحًا على الفور في الحالات التي يتم النظر فيها وكيف يتم التعامل مع كل منها.
نظام الممثل
يعتبر حل مشكلة معقدة وتقسيمها بشكل متكرر إلى مشاكل فرعية أصغر أسلوبًا سليمًا لحل المشكلات بشكل عام. يمكن أن يكون هذا النهج مفيدًا بشكل خاص في علوم الكمبيوتر (بما يتوافق مع مبدأ المسؤولية الفردية) ، حيث إنه يميل إلى إنتاج رمز معياري نظيف ، مع القليل من التكرار أو بدونه ، يسهل الحفاظ عليه نسبيًا.
في التصميم القائم على الممثل ، يسهل استخدام هذه التقنية التنظيم المنطقي للممثلين في هيكل هرمي يُعرف باسم نظام الممثل. يوفر نظام الجهات الفاعلة البنية التحتية التي من خلالها يتفاعل الفاعلون مع بعضهم البعض.
في عكا ، الطريقة الوحيدة للتواصل مع الممثل هي من خلال ممثل ActorRef
. يمثل ActorRef
إشارة إلى جهة فاعلة تمنع الكائنات الأخرى من الوصول مباشرة إلى العناصر الداخلية والحالة الخاصة بهذا الممثل أو التلاعب بها. يمكن إرسال الرسائل إلى ممثل عبر ActorRef
باستخدام أحد بروتوكولات بناء الجملة التالية:
-
!
("أخبر") - يرسل الرسالة ويعود على الفور -
?
("اسأل") - يرسل الرسالة ويعيد المستقبل الذي يمثل ردًا محتملاً
كل ممثل لديه صندوق بريد يتم تسليم الرسائل الواردة إليه. هناك العديد من تطبيقات صندوق البريد للاختيار من بينها ، مع التطبيق الافتراضي هو FIFO.
الممثل يحتوي على العديد من متغيرات الحالة للحفاظ على الحالة أثناء معالجة رسائل متعددة. تضمن Akka أن كل مثيل من الممثل يعمل في سلسلة محادثات خفيفة الوزن الخاصة به وأن الرسائل تتم معالجتها واحدة تلو الأخرى. وبهذه الطريقة ، يمكن الحفاظ على حالة كل ممثل بشكل موثوق دون أن يحتاج المطور إلى القلق الصريح بشأن المزامنة أو ظروف السباق.
يتم تزويد كل ممثل بالمعلومات المفيدة التالية لأداء مهامه عبر واجهة برمجة تطبيقات Akka Actor:
-
sender
: ممثلActorRef
إلى مرسل الرسالة التي تتم معالجتها حاليًا -
context
: المعلومات والأساليب المتعلقة بالسياق الذي يعمل فيه الممثل (بما في ذلك ، على سبيل المثال ، طريقةactorOf
جديد لممثل) - إستراتيجية
supervisionStrategy
: تحدد الإستراتيجية التي سيتم استخدامها للتعافي من الأخطاء -
self
: الممثلActorRef
نفسه
للمساعدة في ربط هذه الدروس معًا ، دعنا نفكر في مثال بسيط لحساب عدد الكلمات في ملف نصي.
لأغراض مثال Akka ، سنحلل المشكلة إلى مهمتين فرعيتين ؛ وهي ، (1) مهمة "فرعية" لحساب عدد الكلمات في سطر واحد و (2) مهمة "الأصل" لتجميع عدد الكلمات في كل سطر للحصول على العدد الإجمالي للكلمات في الملف.
سيقوم الممثل الأصل بتحميل كل سطر من الملف ثم يقوم بتفويض أحد الممثلين الفرعيين بمهمة عد الكلمات في هذا السطر. عندما ينتهي الطفل ، سيرسل رسالة إلى الوالد بالنتيجة. سيتلقى الوالد الرسائل مع عدد الكلمات (لكل سطر) ويحتفظ بعداد للعدد الإجمالي للكلمات في الملف بأكمله ، والذي سيعود بعد ذلك إلى المستدعي عند الانتهاء.
(لاحظ أن نماذج التعليمات البرمجية لـ Akka الواردة أدناه تهدف إلى أن تكون تعليمية فقط ، وبالتالي لا تهتم بالضرورة بجميع شروط الحافة وتحسينات الأداء وما إلى ذلك. أيضًا ، يتوفر إصدار كامل قابل للتجميع من نماذج التعليمات البرمجية الموضحة أدناه في هذا جوهر.)
لنلقِ أولاً نظرة على نموذج تنفيذ لفئة StringCounterActor
:
case class ProcessStringMsg(string: String) case class StringProcessedMsg(words: Integer) class StringCounterActor extends Actor { def receive = { case ProcessStringMsg(string) => { val wordsInLine = string.split(" ").length sender ! StringProcessedMsg(wordsInLine) } case _ => println("Error: message not recognized") } }
هذا الممثل لديه مهمة بسيطة للغاية: استهلاك رسائل ProcessStringMsg
(التي تحتوي على سطر من النص) ، وحساب عدد الكلمات في السطر المحدد ، وإرجاع النتيجة إلى المرسل عبر رسالة StringProcessedMsg
. لاحظ أننا قمنا بتنفيذ فصلنا لاستخدام !
("tell") لإرسال رسالة StringProcessedMsg
(على سبيل المثال ، لإرسال الرسالة والعودة على الفور).
حسنًا ، دعنا نوجه انتباهنا الآن إلى فئة WordCounterActor
الأصل:
1. case class StartProcessFileMsg() 2. 3. class WordCounterActor(filename: String) extends Actor { 4. 5. private var running = false 6. private var totalLines = 0 7. private var linesProcessed = 0 8. private var result = 0 9. private var fileSender: Option[ActorRef] = None 10. 11. def receive = { 12. case StartProcessFileMsg() => { 13. if (running) { 14. // println just used for example purposes; 15. // Akka logger should be used instead 16. println("Warning: duplicate start message received") 17. } else { 18. running = true 19. fileSender = Some(sender) // save reference to process invoker 20. import scala.io.Source._ 21. fromFile(filename).getLines.foreach { line => 22. context.actorOf(Props[StringCounterActor]) ! ProcessStringMsg(line) 23. totalLines += 1 24. } 25. } 26. } 27. case StringProcessedMsg(words) => { 28. result += words 29. linesProcessed += 1 30. if (linesProcessed == totalLines) { 31. fileSender.map(_ ! result) // provide result to process invoker 32. } 33. } 34. case _ => println("message not recognized!") 35. } 36. }
تجري هنا أشياء كثيرة ، لذلك دعونا نفحص كل منها بمزيد من التفصيل (لاحظ أن أرقام الأسطر المشار إليها في المناقشة التالية تستند إلى نموذج الكود أعلاه) ...

أولاً ، لاحظ أنه يتم تمرير اسم الملف المراد معالجته إلى مُنشئ WordCounterActor
(السطر 3). يشير هذا إلى أن الممثل يجب استخدامه فقط لمعالجة ملف واحد. يعمل هذا أيضًا على تبسيط مهمة الترميز للمطور ، من خلال تجنب الحاجة إلى إعادة تعيين متغيرات الحالة ( running
، totalLines
، linesProcessed
، result
) عند انتهاء المهمة ، نظرًا لاستخدام المثيل مرة واحدة فقط (أي لمعالجة ملف واحد) ثم يتم التخلص منها.
بعد ذلك ، لاحظ أن WordCounterActor
يتعامل مع نوعين من الرسائل:
-
StartProcessFileMsg
(السطر 12)- تم الاستلام من الممثل الخارجي الذي بدأ في البداية برنامج
WordCounterActor
. - عند استلامه ، يتحقق
WordCounterActor
أولاً من أنه لا يتلقى طلبًا فائضًا عن الحاجة. - إذا كان الطلب زائدًا عن الحاجة ، يقوم
WordCounterActor
بإنشاء تحذير ولا يتم عمل أي شيء آخر (السطر 16). - إذا لم يكن الطلب زائدًا عن الحاجة:
- يخزن
WordCounterActor
مرجعًا للمرسل في متغير مثيلfileSender
(لاحظ أن هذاOption[ActorRef]
وليسOption[Actor]
- راجع السطر 9). هناك حاجة إلىActorRef
إليها لاحقًا والاستجابة لها عند معالجةStringProcessedMsg
النهائية (التي يتم استلامها منStringCounterActor
، كما هو موضح أدناه). - ثم يقرأ
WordCounterActor
الملف ، وكلما تم تحميل كل سطر في الملف ، يتم إنشاءStringCounterActor
ويتم تمرير رسالة تحتوي على السطر المراد معالجته إليه (الأسطر 21-24).
- يخزن
- تم الاستلام من الممثل الخارجي الذي بدأ في البداية برنامج
-
StringProcessedMsg
(السطر 27)- تم الاستلام من
StringCounterActor
عندما يكمل معالجة السطر المخصص له. - عند الاستلام ، يزيد
WordCounterActor
من عداد السطور للملف ، وإذا تمت معالجة جميع الأسطر في الملف (على سبيل المثال ، عندما تكونtotalLines
وlinesProcessed
متساوية) ، فإنه يرسل النتيجة النهائية إلىfileSender
الأصلي (الأسطر 28-31).
- تم الاستلام من
مرة أخرى ، لاحظ أنه في عكا ، الآلية الوحيدة للتواصل بين الفاعلين هي تمرير الرسائل. الرسائل هي الشيء الوحيد الذي يشاركه الممثلون ، وبما أنه يمكن للممثلين الوصول إلى نفس الرسائل بشكل متزامن ، فمن المهم بالنسبة لهم أن يكونوا غير قابلة للتغيير ، وذلك لتجنب ظروف العرق والسلوكيات غير المتوقعة.
لذلك من الشائع تمرير الرسائل في شكل فئات حالة لأنها غير قابلة للتغيير افتراضيًا وبسبب مدى تكاملها بسلاسة مع مطابقة النمط.
دعنا نختتم المثال بعينة التعليمات البرمجية لتشغيل التطبيق بالكامل.
object Sample extends App { import akka.util.Timeout import scala.concurrent.duration._ import akka.pattern.ask import akka.dispatch.ExecutionContexts._ implicit val ec = global override def main(args: Array[String]) { val system = ActorSystem("System") val actor = system.actorOf(Props(new WordCounterActor(args(0)))) implicit val timeout = Timeout(25 seconds) val future = actor ? StartProcessFileMsg() future.map { result => println("Total number of words " + result) system.shutdown } } }
لاحظ كيف هذه المرة ?
الطريقة المستخدمة لإرسال رسالة. بهذه الطريقة ، يمكن للمتصل استخدام المستقبل المرتجع لطباعة النتيجة النهائية عندما يكون ذلك متاحًا والخروج من البرنامج عن طريق إغلاق ActorSystem.
إستراتيجيات التسامح مع الخطأ والمشرف في عكا
في نظام الممثل ، كل ممثل هو المشرف على أبنائه. إذا فشل أحد الممثلين في التعامل مع رسالة ، فإنه يوقف نفسه وجميع أبنائه ويرسل رسالة ، عادة في شكل استثناء ، إلى مشرفه.
في عكا ، يُشار إلى الطريقة التي يتفاعل بها المشرف ويتعامل معها مع الاستثناءات التي تتسرب إليه من أطفاله على أنها استراتيجية مشرف. استراتيجيات المشرف هي الآلية الأساسية والمباشرة التي تحدد من خلالها السلوك المتسامح مع الخطأ في نظامك.
عندما تصل رسالة تشير إلى وجود فشل إلى مشرف ، يمكن أن يتخذ أحد الإجراءات التالية:
- استئناف الطفل (وأبنائه) ، والحفاظ على حالته الداخلية. يمكن تطبيق هذه الإستراتيجية عندما لا تتلف الحالة التابعة بالخطأ ويمكن أن تستمر في العمل بشكل صحيح.
- أعد تشغيل الطفل (وأطفاله) ، وتنظيف حالته الداخلية. يمكن استخدام هذه الإستراتيجية في السيناريو المعاكس للسيناريو الذي تم وصفه للتو. إذا تعرضت الحالة التابعة للتلف بسبب الخطأ ، فمن الضروري إعادة تعيين حالتها قبل استخدامها في المستقبل.
- وقف الطفل (وأطفاله) نهائيا. يمكن استخدام هذه الاستراتيجية في الحالات التي لا يُعتقد فيها أن حالة الخطأ قابلة للتصحيح ، ولكنها لا تعرض للخطر بقية العملية التي يتم إجراؤها ، والتي يمكن إكمالها في حالة عدم وجود الطفل الفاشل.
- أوقف نفسه وقم بتصعيد الخطأ. يتم توظيفه عندما لا يعرف المشرف كيفية التعامل مع الفشل وبالتالي يقوم بتصعيده إلى مشرفه الخاص.
علاوة على ذلك ، يمكن للممثل أن يقرر تطبيق الإجراء فقط على الأطفال الفاشلين أو على أشقائهم أيضًا. هناك استراتيجيتان محددتان مسبقًا لهذا:
-
OneForOneStrategy
: يطبق الإجراء المحدد على الطفل الفاشل فقط -
AllForOneStrategy
: تطبيق الإجراء المحدد على كافة العناصر التابعة له
إليك مثال بسيط ، باستخدام OneForOneStrategy
:
import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy._ import scala.concurrent.duration._ override val supervisorStrategy = OneForOneStrategy() { case _: ArithmeticException => Resume case _: NullPointerException => Restart case _: IllegalArgumentException => Stop case _: Exception => Escalate }
إذا لم يتم تحديد استراتيجية ، يتم استخدام الاستراتيجية الافتراضية التالية:
- إذا كان هناك خطأ أثناء تهيئة الممثل أو إذا تم قتل الممثل ، يتم إيقاف الممثل.
- إذا كان هناك أي نوع آخر من الاستثناءات ، فسيتم ببساطة إعادة تشغيل الممثل.
إن تنفيذ عكا لهذه الإستراتيجية الافتراضية هو كما يلي:
final val defaultStrategy: SupervisorStrategy = { def defaultDecider: Decider = { case _: ActorInitializationException ⇒ Stop case _: ActorKilledException ⇒ Stop case _: Exception ⇒ Restart } OneForOneStrategy()(defaultDecider) }
تسمح Akka بتنفيذ استراتيجيات المشرف المخصصة ، ولكن كما تحذر وثائق Akka ، قم بذلك بحذر لأن عمليات التنفيذ غير الصحيحة قد تؤدي إلى مشاكل مثل أنظمة الممثلين المحظورة (أي الممثلين الموقوفين بشكل دائم).
شفافية الموقع
تدعم بنية عكا شفافية الموقع ، مما يمكّن الممثلين من أن يكونوا محايدين تمامًا لمكان ظهور الرسائل التي يتلقونها. قد يكون مرسل الرسالة موجودًا في نفس JVM مثل الممثل أو في JVM منفصل (إما يعمل على نفس العقدة أو عقدة مختلفة). تمكن Akka من التعامل مع كل حالة من هذه الحالات بطريقة شفافة تمامًا للممثل (وبالتالي للمطور). التحذير الوحيد هو أن الرسائل المرسلة عبر عقد متعددة يجب أن تكون قابلة للتسلسل.
تم تصميم أنظمة الفاعل للتشغيل في بيئة موزعة دون الحاجة إلى أي كود متخصص. يتطلب Akka فقط وجود ملف التكوين ( application.conf
) الذي يحدد العقد لإرسال الرسائل إليها. فيما يلي مثال بسيط لملف التكوين:
akka { actor { provider = "akka.remote.RemoteActorRefProvider" } remote { transport = "akka.remote.netty.NettyRemoteTransport" netty { hostname = "127.0.0.1" port = 2552 } } }
بعض النصائح حول الفراق ...
لقد رأينا كيف يساعد إطار عمل Akka في تحقيق التزامن والأداء العالي. ومع ذلك ، كما أوضح هذا البرنامج التعليمي ، هناك بعض النقاط التي يجب مراعاتها عند تصميم وتنفيذ نظامك من أجل استغلال قوة عكا إلى أقصى حد:
- إلى أقصى حد ممكن ، يجب تعيين أصغر مهمة ممكنة لكل جهة فاعلة (كما تمت مناقشته سابقًا ، باتباع مبدأ المسؤولية الفردية)
يجب على الجهات الفاعلة معالجة الأحداث (أي معالجة الرسائل) بشكل غير متزامن ويجب عدم حظرها ، وإلا ستحدث تبديل السياق الذي يمكن أن يؤثر سلبًا على الأداء. على وجه التحديد ، من الأفضل إجراء عمليات الحجب (IO ، إلخ) في المستقبل حتى لا تحجب الممثل ؛ بمعنى آخر:
case evt => blockingCall() // BAD case evt => Future { blockingCall() // GOOD }
- تأكد من أن رسائلك كلها غير قابلة للتغيير ، لأن الممثلين الذين يمررونها إلى بعضهم البعض سيعملون جميعًا بشكل متزامن في سلاسل الرسائل الخاصة بهم. من المحتمل أن تؤدي الرسائل المتغيرة إلى سلوك غير متوقع.
- نظرًا لأن الرسائل المرسلة بين العقد يجب أن تكون قابلة للتسلسل ، فمن المهم أن تضع في اعتبارك أنه كلما زاد حجم الرسائل ، كلما استغرق الأمر وقتًا أطول لتسلسلها وإرسالها وإلغاء تسلسلها ، مما قد يؤثر سلبًا على الأداء.
خاتمة
تعمل Akka ، المكتوبة بلغة Scala ، على تبسيط وتسهيل تطوير تطبيقات متزامنة للغاية وموزعة ومتسامحة مع الأخطاء ، مما يخفي الكثير من التعقيد عن المطور. يتطلب تحقيق العدالة الكاملة في عكا أكثر بكثير من هذا البرنامج التعليمي الفردي ، ولكن نأمل أن تكون هذه المقدمة وأمثلةها آسرة بما يكفي لتجعلك ترغب في قراءة المزيد.
أما Amazon و VMWare و CSC فهي مجرد أمثلة قليلة على الشركات الرائدة التي تستخدم عكا بنشاط. قم بزيارة موقع Akka الرسمي لمعرفة المزيد واستكشاف ما إذا كانت Akka يمكن أن تكون الإجابة الصحيحة لمشروعك أيضًا.