چرا پارادیم «اکتور مدل» مهم است؟
همهی ما با برنامهنویسی شیگرا آشنا هستیم. کم و بیش با Aspect Oriented Programming نیز آشنایی داریم. اخیراً Reactive Programming فراگیرتر شدهاست. ولی خیلی از ما چیز زیادی راجع به پارادایم Actor Model نمیدانیم. با اینکه در دوره دانشجویی اشاراتی به این پارادایم شد، ولی من درک خوبی از اهمیت و کاربرد آن پیدا نکردم. در سالهای بعد از دانشگاه هر از چندی گذارم به سایت akka میافتاد ولی زیاد بند نمیشدم. گویا برای یادگیری و استفاده از آن اینرسی داشتم.
در این مطالعات متوجه شدیم بسیاری از پیامرسانهای معروف از «اکتور مدل» استفاده میکنند. عبرتآموز آنکه باز سعی داشتم به جای استفاده از پلتفرمهای اکتوری با ترفندهایی خاصیت اکتورها را شبیهسازی کنم. در نهایت پس از بررسی پیامرسان متنباز actor.im مجاب شدم که باید اکتور مدل را امتحان کنیم. اینجا بود که فهمیدم نسبت به یک پارادایم مهم برنامهنویسی غفلت کامل داشتم.
سعی میکنم برخی از خواص برنامهنویسی با مدل اکتور را توضیح دهم با این امید که توجه بیشتری به این پارادایم مفید و پرکاربرد در ایران ایجاد شود.
از پیامرسان مثال میزنم. میخواهیم تعداد بازدیدهای یک پست را در یک کانال پرمخاطب بشماریم. در روش متعارف اینطور عمل میکنیم:
- یک میکروسرویس مسئول شمردن تعداد بازدید پستهاست. برای مقیاسپذیری (scalability) و دسترسیپذیری (availability) چندین نمونه (instance) از این میکروسرویس بالا میآوریم. این میکروسرویسها کاملاً stateless هستند.
- تعداد بازدیدها را در یک پایگاهداده sql (مثل postgres) یا no-sql (مثل redis) نگه میداریم.
- برای اینکه چند نود یا چند thread مستقل با دستکاری همزمانِ «تعداد بازدید یک پست» خرابکاری نکنند، باید از یک قفل توزیعشده (distributed lock) استفاده شود. یعنی threadای که میخواهد تعداد بازدید را زیاد کند باید block شود تا وقتی اختیار قفل توزیعشده را به دست بیاورد و مطمئن شود thread دیگری روی این رکورد فعال نیست. آنگاه میتواند عدد قبلی را بخواند و عدد جدید را بنویسد.
حالا ببینیم این مساله با مدل اکتور چطور حل میشود.
- یک میکروسرویس اکتوری (مثلاً مبتنی بر akka) میسازیم و چند نمونه (instance) از آن بالا میآوریم. این نودها در یک کلاستر akka قرار میگیرند و با هم هماهنگ میشوند.
- به هر کانال در پیامرسان یک اکتور اختصاص میدهیم. هر اکتور کانال، روی یکی از نودهای کلاستر جانمایی میشود. در واقع اکتور کانال یک شیء (Object) است که رفتار (Behavior) و وضعیت (State) آن کانال را در خود جای میدهد.
- اِعمال و پاسخگویی همه رویدادهای مربوط به یک کانال، به اکتور وی واگذار میشود. رویدادها در یک صف (Mailbox) قرار میگیرند و یکی یکی برای پردازش، تحویل اکتور میشوند.
- وضعیت اکتور، یک شیء معمولی با فیلدهای دلخواه است. مثلا میتواند یک Map از «شناسه پستها» به «تعداد بازدید» داشته باشد. حتی به ConcurrentMap نیاز نیست. چون رویدادها یکی یکی از Mailbox خارج و اعمال میشوند و مشکل همروندی پیش نمیآید.
- هر وقت اکتور یک «رویداد بازدید» از Mailbox بردارد، در Map، تعداد بازدیدهای آن پست را بدون نگرانی از همروندی یکی زیاد میکند. راجع به ذخیرهسازی این دادهها (Persistency) انشاءالله در پست دیگری توضیح خواهم داد.
در این مدل، مساله این است که بتوانیم اکتور مناسب را در کلاستر پیدا کنیم و رویداد را در Mailbox او قرار دهیم. در واقع مساله distributed lock در راه حل کلاسیک، تبدیل به مساله distributed actor lookup میشود. مزیت این مدل آن است که برای مدیریت همروندی نیازی به IO نیست و threadها بلاک نمیشوند. موقع کد زدن نگران همروندی نیستیم. گویا برنامه Single Thread است.
لینک اصلی مطلب