مزایا و معایب معماری Event-Driven یا رویداد محور

مزایا و معایب معماری Event-Driven یا رویداد محور

اگر شما هم یک معمار نرم افزار هستید، به احتمال زیاد نام معماری میکروسرویس (Microservices Architecture) را شنیده اید و یا با آن کار کرده اید. و اگر صرفا از REST به عنوان لایه ارتباطی سرویس خود استفاده کرده باشید، بهتر است بدانید امروزه بیشتر پروژه ها به صورت معماری event-driven (رویداد محور) طراحی می شوند. بنابراین می خواهیم در این مقاله به مزایا و معایب این معماری محبوب، برخی گزینه های کلیدی لازم برای طراحی، و anti-pattern های (ضد الگوهای) رایج بپردازیم.

معماری Microservices Event-Driven چیست؟

در معماری event-driven، وقتی سرویسی بخشی از کاری را انجام می دهد که ممکن است سرویس های دیگر هم از آن بهره مند شوند، آن سرویس یک event (یک رکورد از عمل انجام شده) تولید می کند و سپس سایر سرویس ها آن event را مصرف می کنند تا بتوانند هر تسکی را که به نتیجه event احتیاج دارد، انجام دهند. برخلاف REST، سرویس هایی که event ایجاد می کنند نیازی به دانستن جزئیات سرویس هایی که event را مصرف می کنند، ندارند.
یک مثال ساده: هنگامی که یک سفارش در یک سایت فروش آنلاین قرار می گیرد، یک event "سفارش ثبت شده" تولید می شود و سپس چندین میکروسرویس از آن استفاده می کنند:
1. سرویس سفارش که می تواند یک رکورد سفارش را در پایگاه داده ثبت کند.
2. سرویس مشتری که می تواند رکورد مشتری را ایجاد کند.
3. سرویس پرداخت که می تواند پرداخت را پردازش کند.

event ها را می توان به روش های مختلفی منتشر کرد. به عنوان مثال، می توان آنها را در صف منتشر کرد که تحویل event به مصرف کنندگان مناسب را تضمین می کند، یا می تواند در جریان مدل "pub/sub" منتشر شود که event را منتشر کرده و دسترسی به همه سرویس های ذینفع را امکان پذیر می کند. در هر صورت، تولید کننده، event را منتشر می کند و مصرف کننده نیز آن event را دریافت می کند و بر اساس آن واکنش نشان می دهد. توجه داشته باشید که در برخی موارد می توان این دو بازیگر را ناشر (تولید کننده) و دریافت کننده (مصرف کننده) نیز نامید.

چرا باید از معماری Event-Driven استفاده کرد؟

معماری event-driven مزایای متعددی نسبت به REST ارائه می دهد که شامل موارد زیر است:

Asynchronous

معماری های event-base بدون blocking هستند. این امر باعث می شود هر بخش پس از اتمام تسک خود تسک بعدی را شروع کند، بدون اینکه نگران اتفاقات قبلی یا بعدی باشند. همچنین موجب می شوند event هایی در صف قرار بگیرند که مانع از فشار مجدد مصرف کنندگان eventبه ایجاد کننده آنها و یا مسدود کردن آنها می شود.

Loose Coupling

سرویس ها نباید به یکدیگر وابستگی داشته باشند. هنگام استفاده از event ها، سرویس ها به طور مستقل و بدون نیاز به یکدیگر عمل می کنند؛ از جمله جزئیات اجرای آنها و انتقال پروتکل آنها. سرویس های تحت مدل event می توانند به طور مستقل و راحت تر به روز شوند، تست شوند و دیپلوی گردند.
Easy Scaling: از آنجا که سرویس های تحت یک معماری event-driven جدا از هم هستند و چون سرویس ها معمولاً فقط یک تسک را انجام می دهند، ردیابی یک سرویس خاص و پیمایش آن سرویس آسان می شود.

Recovery support

یک معماری event-driven به کمک صف، می تواند کار از دست رفته را با اجرای مجدد event های گذشته بازیابی کند. این امر می تواند برای جلوگیری از، از دست دادن داده ها هنگامی که یک مصرف کننده ی event نیاز به بازیابی دارد، ارزشمند باشد.
البته معماری های event-driven دارای اشکالاتی نیز هستند. به عنوان مثال ممکن است با جدا کردن بیش از حد وابستگی هایی که در کنار هم به راحتی کار می کردنند، معماری سخت تر شده و موجب پیچیدگی در طراحی، قراردادهای سرویس ها و یا گراف های وابسته بشود.
شاید هم قابل توجه ترین اشکال و چالش این معماری، مدیریت داده ها و transaction ها (تراکنش ها) باشد. به دلیل ماهیتasynchronous ، مدل های event-driven باید با دقت داده های متناقض را بین سرویس ها و ورژن های متفاوت مدیریت کنند. آنها معمولاً از transaction ACID پشتیبانی نمی کنند و به جای آن از eventual consistency پشتیبانی می کنند که پیگیری یا اشکال زدایی آن دشوارتر است.
اما با وجود این اشکالات ، معماری event-driven معمولاً انتخاب بهتری برای سیستم های میکروسرویس است زیرا جوانب مثبت آن مانند طراحی مقیاس پذیر، مستقل بودن سرویس ها و طراحی توسعه پذیر، بیش از نقاط ضعف آن است.

چه زمانی از REST استفاده کنیم؟

با این حال ، مواردی وجود دارد که ممکن است REST هنوز ترجیح داده شود به موارد زیر توجه کنید:
• به یک رابط پرسش و پاسخ محدود به زمان نیاز داشته باشید.
• بخواهید به راحتی از transaction ها پشتیبانی کنید.
• API شما در دسترس عموم باشد.
• پروژه شما کوچک باشد (در این حالت تنظیم و دیپلوی REST بسیار ساده تر است).

مهمترین انتخاب شما طراحی، فریمورک پیام رسانی

وقتی تصمیم به معماری event-driven گرفتید، وقت آن است که فریمورک event خود را انتخاب کنید. نحوه تولید و مصرف event ها، یک عامل کلیدی در سیستم شماست. ده ها فریمورک وجود دارد و انتخاب درست یکی از آنها نیازمند زمان و تحقیق می باشد.
انتخاب اصلی شما به message processing یا stream processing بر می گردد:

Message Processing

در message processing، یک کامپوننت پیامی را ایجاد می کند و سپس آن را به یک مقصد خاص می فرستد. کامپوننت دریافت کننده، پیام را دریافت کرده و مطابق آن عمل می کند. به طور معمول، هنگامی که پیام می رسد، کامپوننت دریافت کننده یک فرآیند واحد را انجام می دهد و سپس، پیام حذف می شود.
یک نمونه رایج از message processing، صف پیام است. در صف های پیام معمولاً از سیستم واسط "store and forward" استفاده می شود که در آن event ها از یک واسط به واسط دیگر منتقل می شوند تا اینکه به مصرف کننده مناسب برسند. ActiveMQ و RabbitMQ دو نمونه معروف از فریمورک های صف پیام هستند. هر دوی این پروژه ها سالهاست استفاده می شوند و بنابراین ثابت شده هستند.

Stream Processing

از طرف دیگر در stream processing، کامپوننت ها هنگام رسیدن به state خاصی، event هایی را تولید می کنند. سایر کامپوننت های ذینفع، این event ها را اصطلاحا listen می کنند و بر اساس آن واکنش نشان می دهند. event ها برای یک گیرنده خاص هدف گذاری نمی شوند، بلکه در اختیار همه کامپوننت های ذینفع قرار دارند.
در stream processing، کامپوننت ها می توانند همزمان به چندین event واکنش نشان دهند و عملیات پیچیده ای را روی چندین stream و event اعمال کنند.
یکی از محبوب ترین فریمورک های stream processing ،Apache Kafka است. کافکا یک راه حل کامل و پایدار است که در بسیاری از پروژه ها استفاده می شود. می توان آن را یک راه حل مناسب برای stream processing در نظر گرفت. کافکا دارای یک پایگاه کاربری بزرگ، یک انجمن مفید و یک مجموعه ابزار کامل است.

گزینه های دیگر

فریمورک های دیگری نیز وجود دارند که ترکیبی از هر دو فریمورک ذکر شده را ارائه می دهند و یا برخی دیگر که راه حل منحصر به فرد خود را دارند. به عنوان مثال، Pulsar (یک پیشنهاد جدیدتر از Apache )، یک سیستم پیام رسان open-source است که هم از stream processing و هم از صف های event با پرفورمنسی بسیار بالا پشتیبانی می کند. Pulsar دارای ویژگی های زیادی ست و بنابراین پیچیده است.
NATS نیز یک سیستم پیام رسان با صف های "synthetic" است. NATS برای ارسال پیام های کوچک و مکرر طراحی شده است که هم پرفورمنس بالا و هم تأخیر کم (low latency) را ارائه می دهد. با این حال، NATS مقداری از دست دادن داده را قابل قبول می داند، در واقع اولویت پرفورمنس را بالاتر از گارانتی تحویل می داند.

سایر ملاحظات طراحی

هنگامی که فرمورک event خود را انتخاب کردید، چندین چالش دیگر وجود دارد که باید در نظر بگیرید:

Event Sourcing

پیاده سازی ترکیبی از سرویس های loosely-coupled، ذخیره داده های متمایز و transaction های غیرقابل تجزیه دشوار است. در این حالت الگویی که ممکن است کمک کند، Event Sourcing است. در Event Sourcing، آپدیت ها و حذف ها هرگز مستقیماً روی داده انجام نمی شوند؛ بلکه تغییر stateهای موجودیت به عنوان رشته ای از event ها ذخیره می شود.

CQRS

event sourcing ای که در بالا به آن اشاره شد، مسئله دیگری را نیز مطرح می کند: از آنجا که state باید از رشته ای از event ها ساخته شود، کوئری ها ممکن است کند و پیچیده شوند. Command Query Responsibility Segregation (CQRS) یک طراحی است که خواستار مدل های جداگانه برای ثبت کردن و خواندن عملیات است.

یافتن اطلاعات event

یکی از بزرگترین چالش ها در معماری event-driven، فهرست کردن سرویس ها و event ها است. توضیحات و جزئیات event ها را از کجا پیدا می کنید؟ دلیل یک event چیست؟ چه تیمی این event را ایجاد کرد؟ آیا آنها به صورت active روی آن کار می کنند؟

طرز برخورد با تغییر

آیا الگوی event ای تغییر خواهد کرد؟ چگونه می توانید یک الگوی event را تغییر دهید بدون اینکه سرویس های دیگر را خراب کنید؟
نحوه پاسخ دادن به این سوالات با افزایش تعداد سرویس ها و event ها، حیاتی می شود.

anti-pattern ها

همانند اکثر معماری ها، معماری event-driven نیز مجموعه ای از anti-pattern های خاص خود را دارد. در اینجا چند مورد هستند که باید مراقب آنها باشید:

زیادش خوب نیست

مراقب باشید که event های بیش از اندازه ایجاد نکنید. ایجاد event های بسیار زیاد، پیچیدگی های غیرضروری بین سرویس ها ایجاد می کند، بار شناختی را برای توسعه دهندگان افزایش می دهد، دیپلوی کردن و تست کردن را دشوارتر می کند و باعث تراکم مصرف کنندگان event می شود. بنابراین دقت داشته باشید که لازم نیست هر متدی یک event داشته باشد.

event های کلی

از درست کردن event های با نامی که وظیفه و علت ساختش را نمی رساند و یا با ماموریت های کلی، پرهیز کنید. تیم های دیگر دلیل وجود event شما، اینکه برای چه چیزی باید استفاده شود و زمان استفاده از آن را باید متوجه بشوند. event ها باید هدف خاصی داشته باشند و بر این اساس نامگذاری شوند. event هایی با نام کلی یا event های کلی با flag های گیج کننده، باعث بروز مشکلاتی می شود.

گراف های وابسته پیچیده

مراقب سرویس هایی باشید که به یکدیگر وابسته هستند و نمودارهای وابستگی پیچیده یا لوپ های برگشتی ایجاد می کنند. توجه داشته باشید که هر نقطه ای در شبکه که event شما قرار است از آن عبور کند تأخیری را به درخواست اصلی اضافه می کند.

بهینه کردن زود هنگام

بیشتر محصولات در ابتدا کوچک هستند و با گذشت زمان رشد می کنند. گرچه ممکن است بخواهید نیازهای آینده را در محصول خود مدنظر داشته باشید اما اگر تیم شما کوچک باشد، پیچیدگی اضافی معماری event-driven ممکن است شما را کٌند کند. در عوض، سیستم خود را با معماری ساده ای شروع کنید اما جداسازی لازم بخش های مختلف را در نظر داشته باشید تا بتوانید با رشد نیازهای خود آنها را تغییر دهید.

توقع برطرف کردن همه مشکلات توسط event-driven

در سطح فنی پایین، انتظار نداشته باشید که معماری event-driven همه مشکلات شما را برطرف کند. گرچه این معماری مطمئناً می تواند بسیاری از زمینه های اختلال عملکرد فنی را بهبود بخشد، اما نمی تواند مشکلات اساسی مانند عدم تست خودکار، ارتباط ضعیف تیمی یا شیوه های منسوخ توسعه برنامه را برطرف کند.

از بهترین نوشته‌های کاربران سکان آکادمی در سکان پلاس


online-support-icon