چرا پارادایم اکتور مدل مهم است؟

Actor Model with Akka
Actor Model with Akka

بسم الله الرحمن الرحیم

‫همه ما با برنامه نویسی شیء گرا آشنا هستیم. کم و بیش با Aspect Oriented Programming آشنایی داریم. اخیراً Reactive Programming فراگیرتر شده است. ولی خیلی از ما چیز زیادی راجع به پارادایم Actor Model نمی دانیم. با اینکه در دوره دانشجویی اشاراتی به این پارادایم شد، ولی من درک خوبی از اهمیت و کاربرد آن پیدا نکردم. در سالهای بعد از دانشگاه هر از چندی گذارم به سایت akka می افتاد ولی زیاد بند نمی شدم. گویا برای یادگیری و استفاده از آن اینرسی داشتم.

اواخر سال 96 مسئولیت پژوهش درباره «معماری مقیاس پذیر پیام رسان» به شرکت اعوان سپرده شد. در این مطالعات متوجه‫ شدیم بسیاری از پیام رسانهای معروف از «اکتور مدل» استفاده می کنند. عبرت آموز آنکه باز سعی داشتم، به جای استفاده از پلتفرمهای اکتوری با ترفندهایی خاصیت اکتورها را شبیه سازی کنم. در نهایت پس از بررسی پیام رسان متن باز actor.im مجاب شدم اکتور مدل را امتحان کنیم. اینجا بود که فهمیدم نسبت به یک پارادایم مهم برنامه نویسی غفلت کامل داشتم.

‫ سعی میکنم برخی از خواص برنامه نویسی با مدل اکتور را توضیح دهم با این امید که توجه بیشتری به این پارادایم‫ مفید و پرکاربرد در ایران ایجاد شود.

‫مزیت اول - راه حل متفاوتی برای مدیریت همروندی (Concurrency Management)

از پیام رسان مثال میزنم. میخواهیم تعداد بازدیدهای یک پست در یک کانال پرمخاطب را بشماریم. در روش متعارف‫ اینطور عمل میکنیم.

  1. ‫یک میکروسرویس مسئول شمردن تعداد بازدید پستهاست. برای مقیاس پذیری (scalability) و دسترسی پذیری (availability) چندین نمونه (instance) از این میکرسرویس بالا می آوریم. این میکروسرویسها کاملاً stateless هستند.
  2. ‫تعداد بازدیدها را در یک پایگاه داده sql (مثل postgres) یا no-sql (مثل redis) نگه میداریم.
  3. ‫برای اینکه چند نود یا چند thread مستقل با دستکاری همزمانِ «تعداد بازدید یک پست» خرابکاری نکنند، باید از یک قفل توزیع شده (distributed lock) استفاده شود. یعنی thread ای که میخواهد تعداد بازدید را زیاد کند باید block شود تا وقتی اختیار قفل توزیع شده را به دست بیاورد و مطمئن شود thread دیگری روی این رکورد فعال نیست. آنگاه می تواند عدد قبلی را بخواند و عدد جدید را بنویسد.

حالا ببینیم این مساله با مدل اکتور چطور حل میشود.

  1. ‫یک میکروسرویس اکتوری (مثلاً مبتنی بر akka) میسازیم و چند نمونه (instance) از آن بالا می آوریم. این نودها در یک کلاستر akka قرار می گیرند و با هم هماهنگ میشوند.
  2. ‫به هر کانال در پیام رسان یک اکتور اختصاص می دهیم. هر اکتور کانال روی یکی از نودهای کلاستر جانمایی میشود. در واقع اکتور کانال یک شیء (Object) است که رفتار (Behavior) و وضعیت (State) آن کانال را در خود جای میدهد.
  3. ‫اِعمال و پاسخگویی همه رویدادهای مربوط به یک کانال، به اکتور وی واگذار میشود. رویدادها در یک صف (Mailbox) قرار می­گیرند و یکی یکی برای پردازش تحویل اکتور میشوند.
  4. ‫وضعیت اکتور، یک شیء معمولی با فیلدهای دلخواه است. مثلاً می تواند یک Map از «شناسه پستها» به «تعداد بازدید» داشته باشد. حتی به ConcurrentMap نیاز نیست. چون رویدادها یکی یکی از Mailbox خارج و اعمال میشوند، مشکل همروندی پیش نمی ­آید.
  5. ‫هر وقت اکتور یک «رویداد بازدید» از Mailbox بردارد، در Map، تعداد بازدیدهای آن پست را بدون نگرانی از همروندی یکی زیاد میکند. راجع به ذخیره سازی این داده ها (Persistency) ان شاء الله در پست دیگری توضیح خواهم داد.

‫در این مدل، مساله این است که بتوانیم اکتور مناسب را در کلاستر پیدا کنیم و رویداد را در Mailbox او قرار دهیم. در واقع مساله distributed lock در راه حل کلاسیک، تبدیل به مساله distributed actor lookup میشود. مزیت این مدل آن است که برای مدیریت همروندی نیازی به IO نیست و thread ها بلاک نمی شوند. موقع کد زدن نگران همروندی نیستیم. گویا برنامه Single Thread است.

‫ ان شاء الله در پستهای بعدی به مزیتهای دیگر اکتور مدل خواهیم پرداخت.

‫ برای آشنایی بیشتر با مدل اکتور اینجا را ملاحظه بفرمایید. اگر علاقه مند به کاری چالشی با akka هستید، اینجا منتظر شما هستیم.