Masoud Bahrami
Masoud Bahrami
خواندن ۱۰ دقیقه·۵ سال پیش

Orchestration یا Choreography?

1-مقدمه:

با شکستن یک برنامه یک-تکه(مانولیت) به مجموعه‌ای از سرویس های کوچکتر ما با مسائل جدید مواجه می‌شویم که ذاتی سیستم‌های توزیع شده می‌باشد و باید برای آن‌ها راهکاری داشته باشیم. یکی از این مسائل بحث تراکنش هایی است که قبلاً در سطح یک پراسس و یک دیتابیس(معمولا)مدیریت می‌شد و در حال حاضر این تراکنش بین چندین سرویس پخش شده است. به عنوان مثال یک سیستم مدیریت سفارش در یک سیستم گردشگری آنلاین را در نظر بگیرید. فردی درخواست سفارش هتل و پرواز را دارد. در صورتی که پس از رزرو پرواز مشخص شود که برای فرد هتلی در روز درخواست شده با ظرفیت کافی در دسترس نیست باید عملیات قبلی(پرواز رزرو شده) کنسل شود(Roll back). عملیات رزرو(هتل و پرواز) تنها زمانی نهایی خواهد شد که فرد اقدام به پرداخت صورتحساب نماید(مسئله عنوان شده در اینجا چه در حالتی که یک برنامه یک-تکه داریم و چه در حالت داشتن چندین سرویس یک مسئله از نوع Long Live Transaction(LLT) می باشد.).

قبل از شکستن برنامه یک-تکه به مجموعه‌ای از سرویس های کوچکتر تمام ماژول های مورد نیاز همه در یک پراسس اجرا می‌شدند و در نتیجه به هنگام نیاز به تعامل و صحبت بین ماژول های مختلف ما با صدا زدن‌های درون سرویسی (In-Process Communication) سروکار داشتیم. به عنوان مثال ماژول Order با درخواست ماژول مدیریت هتل(به عنوان مثال با درخواست اینترفیس آن و تزریق از طریق سازنده) می‌توانست براحتی با ماژول مدیریت هتل ارتباط برقرار کند و درخواست رزرو هتل را به ماژول مورد نظر delegate کند.

ماژول های برنامه یک-تکه
ماژول های برنامه یک-تکه

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


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

2-روش Orchestration

در این روش یک نقطه مرکزی جهت مدیریت  بخاطر سپاری وضعیت‌های مختلف تشکیل‌دهنده تراکنش کلی وجود دارد. این نقطه ی مرکزی تمام گام ها جهت تکمیل(موفقیت آمیز یا شکست آمیز) یک تراکنش همانند مثالی که عنوان شده را بصورت کامل می داند. نقطه ی شروع هر گام و همچنین سرویسی که وظیفه ی انجام آن گام را بر عهده دارد را می‌شناسد. در مثال عنوان شده نقطه ی مرکزی ابتدا درخواست رزرو هتل را به سرویس هتل می‌فرستد. در این حالت سفارش مشتری در حالت در انتظار رزرو هتل می باشد. با تکمیل هر گام سفارش یک وضعیت جدید پیدا می کند. پس از دریافت پیغام موفقیت آمیز رزرو هتل از سرویس هتل وضعیت سفارش به رزرو هتل تغییر می کند. سپس نقطه ی مرکزی درخواست سفارش پرواز را به سرویس مدیریت پرواز ارسال می کند. و وضعیت می‌تواند به وضعیت “در انتظار رزرو پرواز” تغییر کند. پس از دریافت پیغام موفقیت آمیز رزرو هواپیما توسط سرویس مدیریت پرواز وضعیت سفارش به رزرو هواپیما تغییر کرده و سپس درخواست به سیستم پرداخت جهت پرداخت هزینه سفارش ارسال می شود. پس از پرداخت موفقیت آمیز هزینه سفارش، سفارش به وضعیت تکمیل شده تغییر وضعیت می دهد. در صورتی که در هر مرحله مشکلی بوجود بیاد نقطه ی مرکزی می‌تواند با طی کردن بر عکس گام های سپری شده و ارسال درخواست های لغو کردن سفارش(  ‍Request Compensating) اقدام به لغو تدریجی منابع سفارش شده و در نتیجه لغو کردن کل درخواست سفارش کند. نقطه ی مرکزی در این روش Orchestrator نامیده می شود.

2-1-مزایای این روش:

  1. تمام مراحل یک تراکنش بصورت مشخص و واضع (Explicit) مشخص شده اند. در نتیجه دیباگ کردن و همچنین درک و فهم آن راحت و آسان می باشد.
  2. توسعه آن راحت می باشد. در صورتی که مراحل رزرو بالا پس از مدتی تغییر کند و مثلاً یک مرحله ی سرویس های V.I.P. فرودگاه نیز به سفارش اضافه می‌شود به راحتی می‌توان به تغییر الگوریتم گام های Orchestrator این مورد را پیاده‌سازی کرد.
  3. این روش بیان دیگری از روش Workflow-state است و به همین دلیل می‌توان آن را با سرویس های workflow-state براحتی پیاده‌سازی کرد.

2-2-اما معایب این روش:

  1. داشتن یک نقطه ی شکست مرکزی. در صورتی که Orchestrator از مدار خارج شد کل فرآیند متوقف خواهد شد.
  2. تعداد پیام های رد و بدل شده در این روش در حالت پیش فرض که همه ی مراحل بدون مشکل و بصورت کامل تکمیل شوند (2n) می باشد. که n تعداد سرویس های درگیر در این فرآیند می باشند.
  3. داشتن وابستگی زیاد بین Orchestrator با سایر سرویس ها. در این حالت Orchestrator  با سایر سرویس ها دچار عارضه ی tight-coupling خواهد شد. همچنین توسعه سایر سرویس ها نیز وابسته به Orchestrator خواهد شد. به عنوان مثال فرض کنید تغییری در API سرویس مدیریت هتل انجام شده است. و این تغییرات توسط تیم توسعه‌دهنده پابلیش شده است. اما تا زمانی که این تغییر در Orchestrator نیز اعمال نشود قابل مشاهده توسط کاربر پایانی نخواهد بود.

منتقدان روش بالا بخصوص با تأکید بر مشکل دوم این روش را تقبیح کرده و بصورت افراطی سعی در استفاده از روش دوم که در زیر توضیح داده خواهد شد دارند.

3-روش Choreography

در این روش با حذف نقطه ی مرکزی سعی می‌شود که نقطه ی مرکزی شکست را از بین برد و همچنین وابستگی بین سرویس ها را نیز از بین برد(خوشحال نباشید در این باره توضیح خواهم داد). در اینجا هر سرویس پس از انجام وظیفه ی خود در قالب یک ایونت به سایر سرویس ها اعلام می‌کند که کارش را انجام داده است. مثلاً سرویس مدیریت هتل پس از رزرو هتل با ارسال یک ایونت اعلام می‌دارد که یک هتل در تاریخ مشخص برای یک مشتری خاص رزرو شده است. سایر سرویس ها با گوش دادن به این ایونت فرآیند را ادامه خواهند داد. مثلاً در اینجا سرویس مدیریت سفارش وضعیت سفارش را تغییر خواهد داد. و سپس سرویس مدیریت پرواز عملیات خود را شروع می کند.


همانطور که ملاحضه می کنید در این روش نقطه مرکزی که در مورد آن نگرانی داشتیم از بین رفته است. سرویس های بصورت loosely couple با یکدیگر ارتباط دارند. ارتباط بین سرویس ها بصورت ارسال مسیج و گوش داد به مسیج ها در طرف دیگر ارتباط می باشد.

3-1-مزیت ها:

  1. از بین رفتن Single Point of Failure
  2. وابستگی توسعه بین سرویس ها از بین می رود. سرویس های جهت توسعه ویژگی جدید نیازی نیست که منتظر بروزسانی نقطه ی مرکزی باشند
  3. در صورت نیاز به اضافه کردن یک سرویس جدید که قرار است به عنوان مثال پس از رزرو موفق هتل یک پیام به فرد رزرو کننده ارسال کنند، به آسانی قابل انجام می باشد. کافی یک سرویس جدید ارسال ایمیل داشته باشیم که به مسیجی که توسط سرویس رزرو هتل ارسال می شود گوش فرا داده و متناسبا عملیات ارسال ایمیل را انجام دهد.
  4. بدلیل نداشتن نقطه ی مرکزی اسکیل کردن سرویس ها آسانتر انجام می شود.
  5. تعداد پیام های رد و بدل شده در این روش کمتر می باشد. در بهترین حالت تعداد پیام ها n تا می باشد.

3-2-معایب:

  1. تمامی فرآیندهای مهم سیستم بصورت ضمنی و implicit مشخص شده اند. در نتیجه فهم این فرآیندها مشکل تر خواهد بود. بخصوص برای توسعه دهندگان جدیتر
  2. فرآیند اشکال زدایی مشکل تر خواهد بود. به عنوان شما نیاز به راهکارهای بیشتری از جمله داشتن Aggregate Log خواهید داشت.
  3. نسبت به روش قبلی کمی نسبت به ذهنیت غالب توسعه دهندگان جدیدتر است و نیاز به یادگیری بیشتری دارد.

4-مقایسه:

یکی از نکات مهمی که در مورد این دو روش  بیشتر مورد تاکید می باشدبحث وابستگی شدید/ضعیف بین سرویس ها  است. ابتدا باید توجه داشت که بین اجزای تشکیل دهنده یک فرآیند گاها بصورت ذاتی وابستگی وجود دارد و در نتیجه حذف و از بین بردن واسبتگی در این مسائل اساسا بی معنی است. ما فقط می توانیم این جهت این وابستگی را تغیر دهیم.

یکی از ایراداتی که به روش اول گرفته می شود وابستگی سرویس ها که در روش دوم ظاهرا این مشکل حل شده است. اما در روش دوم ما با یک چالش بسیار مهم تر مواجه هستیم. در روش دوم هر سرویس باید وابستگی های ورودی و خروجی خود که بصورت implicit بوجود آمده را کاملا بداند. مثلا سرویس پرداخت صورتحساب باید بداند که چه موقع باید برای انجام عملیات پرداخت تریگر شود(وابستگی های خروجی از سرویس پرداخت به سایر سرویس ها) و هم اینکه باید خبر داشته باشد که چه سرویس هایی احیانا پس از اتمام عملیات پرداخت نسبت به نتایج آن و ادامه فرآیند علاقمند هستند و بر اساس آن اقدام به ارسال مسیج اتمام موفقیت آمیز عملیات پرداخت نماید.(واسبتگی های ورودی از سایر سرویس ها).

بین اجزای تشکیل دهنده یک فرآیند گاها بصورت ذاتی وابستگی وجود دارد و در نتیجه حذف و از بین بردن واسبتگی در این مسائل اساسا بی معنی است.

بیایید یک مثال دیگر را در بررسی کنیم. فرض کنید میخواهیم کمی فرآیند را تغییر دهیم و مثلا فرآیند رزرو هتل(که در حال حاضر اولین فرآیند است) و فرآیند بلافاصل بعد از آن یعنی رزرو بلیط هواپیما را جابجا کنیم. این تغییر بر روی حداقل دو تا سرویس پخش خواهد شد و دو سرویس تحت تاثیر این تغییر خواهند بود. تاثیر این مورد وقتی تیم های مختلفی درگیر توسعه این دو سرویس باشند مشخص تر و نمایان تر خواهد شد. تغییر که دو تیم مختلف را درگیر خواهد کرد.

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

به عنوان مثال آخری فرض کنید که سرویس V.I.P. نیز جزو خدماتی بود که پس از رزرو هتل و هواپیما کاربر امکان افرودن آن به سبد خرید را داشت. اما پس از مدتی تصمیم گرفته می شود که این سرویس فقط برای کاربرانی قابل استفاده باشد که سبد خرید آنها به یک مینیمم قیمتی رسیده باشد. به عنوان مثال در صورتی که سبد خرید حداقل 1200000 تومان رسیده باشد کاربر می تواند سرویس V.I.P. فرودگاهی را انتخاب کند در غیر اینصورت باید وارد سرویس پرداخت شده و اقدام به پرداخت نماید.  در اینحالت با این چالش مواجه خواهیم شد که این عملیات باید کجا کنترل شود. آیا باید در فرآیند خرید هواپیما چک شود در اینصورت چگونه باید سرویس V.I.P. رد شود. و اینکه سرویس پرداخت باید به چه مسیج هایی گوش دهد و چه موقع باید تریگر شود.


5-کدام روش بهتر و مناسب تر است. و کدام روش را باید انتخاب کنیم؟

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

بصورت کلی ترکیبی از Orchestration و  Choreography بهترین گزینه در اکثر موارد می باشد.

این مقاله نخستین بار در این آدرس منتشر شد.

microservicessoasagaservice composition
شاید از این پست‌ها خوشتان بیاید