میکروسرویس یا مونولیت(خوب، بد، چُخ)

تا حالا شده که ویدیوی یک کنفرانس معتبر را ببیند که موضوع آن درباره این باشد که چقدر میکروسرویس‌ها خوب هستند و چطور شرکت‌های بزرگ مشکلات را به وسیله میکروسرویس‌هایی که به صورت کانتینری اجرا شده، حل کردند، بعد با خودتان فکر کنید چرا ما این کار را نکنیم؟

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

معماری میکروسرویس

در معماری میکروسرویس، به صورت کلی شما Domain Model پروژه را به سرویس‌های کوچکتری که کمتر به هم وابسته هستند و می‌توانند مستقل از هم دیپلوی و اسکیل بشوند تقسیم می‌کنید. هرکدام از این سرویس‌ها به تنهایی یک نرم‌افزار با معماری مخصوص به خودشان هستند.

سرویس‌ها در مواردی اندپوینت‌های REST را فراهم می‌کنند که می‌تواند توسط یک API Gateway به کار گرفته‌شوند یا سرویس‌ها ممکن است از طریق یک سیستم پیام‌رسان با یکدیگر صحبت کنند. بعضی از این سرویس‌ها دارای رابط کاربری وب هستند و برخی از آن‌ها فقط و فقط یک API ارائه می‌دهند.

در معماری میکروسرویس محور، هر سرویس مسئول مدیریت اطلاعات خود است و ایده‌آل‌ گرایانه نباید ساختار بانک اطلاعاتی خود را با دیگر سرویس‌ها به اشتراک قرار دهد. این معماری فراهم کردن کلاینت‌های موبایل و دسکتاپ را که می‌توانند مستقل و بسته به نیاز Scale بشوند را ساده می‌کند.

برای ساخت میکروسرویس‌ها الگوهای مختلفی وجود دارد، هر الگوی انتخاب شده، مسئول انتخاب روش ارتباط که سرویس‌ها با یکدیگر و  تجمیع اطلاعات می‌شود.

کریس ریچاردسون این لگو‌ها را در مقاله زیر به خوبی توضیح داده‌است که پیشنهاد می‌کنم آن را مطالعه کنید.

https://microservices.io/patterns/microservices.html

خوب چیزی که در ادامه می‌خواهم به آن بپردازم این است که منافع و مشکلات معماری میکروسرویس‌ها را ببینید تا بدانید که چه زمانی باید به سراغ آن‌ها برویم و چه زمانی باید از آن‌ها فرار کنیم!

منافع رایج در معماری میکروسرویس

به صورت کلی، میکروسرویس‌ها بسته به اینکه چطور پیاده سازی شوند منافع زیر را در سه دسته ایجاد می‌کنند و یا حداقل این طور ادعا دارند که می‌توانند ایجاد کنند:

  • وابستگی کم
  • اسکیل پذیری عالی
  • دوره های توسعه و انتشار سریعتر

وابستگی کم

کامپوننت‌ها در معماری میکروسرویس‌ها از معماری‌های سنتی، کمتر به هم وابستگی دارند. این سیستم‌ها از معماری‌های پیام محور‌(Message-Driven) و یا اتفاق محور‌(Event-Driven) برای ایجاد ارتباط بین این کامپوننت‌های استفاده می‌کنند که منجر به ایزوله شدن کامپوننت‌های سیستم، تست کردن راحت‌تر و اجرای سریع‌تر آن‌ها می‌شوند. این سیستم‌ها همچنین منافع دیگری را ایجاد می‌کنند، به طور مثال، وجود یک باگ Memory Leak در یک میکروسرویس ایزوله شده، منجر به خرابی کل اپلیکیشن و یا افتادن کل سرویس نمی شود.در یک کلام معماری میکروسرویس باعث کاهش تعداد Single point of failure در سیستم خواهد شد.

باید بگویم که کامپوننت‌ها با وابستگی کم، به نسبت یک سیستم مونولیتیک خیلی سریعتر اجرا می‌شوند و در نتیجه با اجرای موازی اجزاي سیستم، می‌توان سرعت استارت را برای سیستم‌های بزرگ‌تر بالا برد. همچنین به دلیل این وابستگی کم، هر کدام از سرویس‌ها می‌توانند انتخاب کنند که از هر دیتابیس و یا دیتا استوری که برای حوزه کار خود مناسب‌تر هستند، استفاده کنند که همانطور که می‌دانید در خیلی مواقع در یک سیستم مونولیتیک توافق می‌شود که فقط از یک نوع دیتابیس استفاده شود. برای مثال در یک سرویس با مقدار زیادی از داده‌های بدون ساختار می‌توانید از یک بانک اطلاعات NoSQL استفاده کنید در حالی که یک سرویس دیگر که با داده‌های ساختارمند و Transaction سر و کار دارد می‌تواند سراغ بانک‌های اطلاعاتی RDBMS برود.

دوره های توسعه و انتشار سریعتر

در یک معماری میکروسرویس که خوب پیاده سازی شده‌باشد:

  • دوره‌های توسعه نرم‌افزار سریع‌تر  و زمان آماده سازی یک ویژگی جدید و یا تغییر ویژگی‌های موجود خیلی کوتاه‌تر می‌شود. یک سیستم با دومین پیچیده به راحتی می‌تواند با تقسیم به سرویس‌های قابل مدیریت و قابل فهم در طولانی مدت بهتر قابل مدیریت باشد.
  • بکارگیری تکنولوژی‌های جدیدتر ساده‌تر است، اجزای سیستم می‌تواند جداگانه به صورت مرحله به مرحله بروزرسانی شوند و در نتیجه می‌توان در هرکدام از این کامپوننت‌ها از stack مختص به خود استفاده کنند.
  • همچنین این امکان وجود خواهد داشت که هریک از این میکروسرویس‌ها از زبان‌های مختص به خود استفاده کنند و با استفاده از ساختارهای معمول ارتباط مثل gRPC و یا Message Queue و یا Pub/Sub با یکدیگر ارتباط بگیرند،‌که در نهایت شما را قادر خواهد ساخت تا از تیم های مختلف با زبان‌های مختلف استفاده کنید و وابستگی مجموعه را به یک زبان خاص کمتر می‌کند.
  • تیم‌های اجرایی کمتر به همدیگر وابسته خواهند بود، زیرا ارتباط بین اجزای سیستم توسط API و یا یک ساختار قراردادی دیگر اتفاق می‌افتد و هر تیم قادر است با حفظ ساختار قراردادی روال‌های داخلی کد خود را تغییر دهد بدون اینکه نگران باشد، این کار منجر به خراب شدن کد تیم دیگر بشود.
  • روال مناسبی برای تیم‌های چابک فراهم می‌سازد، در این ساختار تیم‌ها می‌توانند تمرکز را روی سرویس‌های خود قرار دهند و فقط نگران یک سرویس باشند.

اسکیل پذیری عالی

  • یکی از مهمترین منافع از معماری میکروسرویس امکان تغییر اسکیل هریک از اجزا، بسته به بار و فشار روی هریک از آن‌ها است. اگر این بخش به خوبی پیاده‌سازی شود منجر به تقسیم بار ایده‌آل بین اجزای سیستم و کاهش هزینه زیرساخت‌های مورد نیاز می‌شود. سرویس‌ها با تقاضای بیشتر می‌توانند Scale up شوند و سرویس‌های که تقاضای آن‌ها کمتر شده‌، می‌توانند Scale Down شوند بدین‌ترتیب می‌توان منابع را بهتر مدیریت کنیم.
  • دیپلوی کردن سرویس‌ها به صورت مستقل باعث می‌شود کل نرم‌افزار قابلیت اطمینان بالاتری داشته‌باشد و ایجاد اصلاحات جزئی ساده‌تر شود و نیازی به بروزرسانی کل نرمافزار برای رفع مشکل در یک سرویس نخواهیم بود.
  • مدل های پیچیده تری از Scaling قابل اجرا خواهد بود، سرویس‌های حیاتی بطور موثرتری اسکیل خواهند شد.
  • بارگذاری پیوسته در سیستم‌های پیچیده در این معماری به نسبت معماری Monolithic خیلی آسان‌تر است به دلیل کوچکتر بودن اجزا، پیدا کردن و رفع مشکلات در هریک از این کامپوننت‌ها بسیار آسان‌تر می‌شود.

مشکلات رایج در معماری میکروسرویس

خوب در هر معماری یک سری از مشکلات وجود دارد و میکروسرویس‌ها هم از این قاعده مستثنی نیستند.

پیچیدگی

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

  • استک‌های استفاده شده مختلف در هریک از اجزای سیستم، تیم اجرایی رو مجبور می‌کند تا زمان بیشتری رو صرف یادگیری این زبان‌ها کنند.
  • اسکیل کردن خیلی موثر‌تر است، اما به امکانات پیشرفته‌تری مانند Service Discovery،DNS Routing و دیگر امکاناتی از این دست نیاز خواهد بود.
  • ارتباط بین اجزای سیستم ممکن است به سیستم‌های پیام‌رسان نیاز داشته‌باشد (Queue, Pub/Sub, Event Store )
  • تراکنش‌ها در این سیستم توزیع یافته پیچیده‌تر است، هرگونه Rollback در این سیستم‌ها به دلیل نیاز به انجام Rollback در بانک‌های اطلاعاتی مختلف در سرویس‌های مختلف می‌توانند پیچیدگی و احتمال ایجاد خطا در آن‌ها بالاتر ببرند.
  • بارگذاری کل نرم‌افزار روال پیچیده‌تری است، پیچیدگی‌هایی مثل کار با Container ها و Orchestration و مجازی‌سازی اضافه می‌شود.
  • به زیر ساخت‌های پیچیده‌تری نیاز داریم‌،‌ در اغلب موارد به کانتینرها (Docker) و سیستم‌های Orchestration مانند Kubernetes نیاز پیدا می‌کنیم.

تست‌های Integration

انجام تست‌های End-to-End و Integration Tests به دلیل وجود بخش‌های مختلف، و ارتباطات پیچیده‌تر بین آن‌‌ها پیاده‌سازی زیرساخت‌های تست و بروز نگهداری آن‌ها خیلی مشکل‌تر خواهد بود.

اندازه تیم و تجربه آن‌ها

استک فنی مورد استفاده در میکروسرویس‌ها پیچیده‌تر هستند و یادگیری ‌آنها مشکل‌تر است و در نتیجه تیم با تجربه‌تری و به تعداد بیشتری از افراد با مجموعه توانایی‌های Senior-level در مقایسه با یک سیستم Monolithic نیاز است. در این حالت شما تیم بزرگتری برای نگهداری نرم‌افزار خود نیاز دارید. زیرا تکنولوژی‌ها و کامپوننت‌های بیشتری وجود دارند.

پیاده سازی Feature هایی که منجر به تغییر در سیستم‌های مختلف هستند زمان بیشتری نیاز دارند زیرا زمان بیشتری صرف توافق در مورد قراردادها و API ها خواهد شد.

با وجود اینکه اعضای تیم بسته به کامپوننتی که روی آن کار می‌کنند، Skill set گسترده‌ای دارند ولی لزوما دید کلی و جامعی بر کلیت Application ندارند و ممکن است برطرف کردن نیازهای کسب و کار و یا پیدا کردن و رفع مشکل‌هایی را که از ارتباط بین اجزا بوجود آمده‌اند، سخت تر کند.

سربار

  • میکرو سرویس‌های پیچیده، به دلیل پیاده‌سازی روال‌های مانیتورینگ، سیستم‌های پیام رسان، Orchestration و سرویس رجیستری و مواردی از این دست کار سربار بیشتری داشته باشند.
  • توسعه اولیه سیستم ممکن است به دلیل این پیچیدگی‌ها طولانی‌تر باشد و زمان رسیدن به بازار محصول را به عقب بندازد.
  • هزینه اولیه زیر ساخت ممکن است نسبت به معماری Monolithic خیلی بیشتر شود.
  • در ساختارهای میکروسرویس، همیشه مقداری از تکرار کد بین چند سرویس وجود دارد که می‌تواند به عنوان یک سربار در نظر گرفته‌شود.

کی نباید از معماری میکروسرویس استفاده کرد‌؟

به یاد داشته باشید تا زمانی که مجبور نشدید، از این معماری نباید استفاده کنید. هر نرم‌افزاری نیازمندی‌های Scaling در حد Google،‌Netflix، Amazon و یا Spotify را ندارد. خیلی از منافع میکروسرویس‌ها برای این مجموعه‌ها به دلیل شرایط خاص Scaling آنهاست که شاید برای شرایط شما اصلا محلی از اعراب نداشته باشد.

برخی از دلایلی که شاید برای شما بهتر است به ساختارهای Monolithic بچسبید:

  • اگر اسکوپ نرم‌افزار شما کوچک است و قرار نیست در زمان کوتاه به اندازه‌های مثل شرایط فعلی آپارات برسید. برای نرم‌افزارهای با کاربرد مشخص که به خوبی تعریف شده‌باشند، استفاده از monolith همیشه بهترین گزینه هستد مثل:
  1. نرم افزار CRUD محوری برای استفاده داخلی شرکت
  2. نرم افزارهای کوچک با حوزه استفاده خیلی مشخص، مثل یک سایت فروش آنلاین برای حوزه خاصی از محصولات
  • وقتی زمان رسیدن محصول به بازار برای نرم‌افزار جدید خیلی حیاتی باشد، باید بدانید که زمان Time to Market برای معماری میکروسرویس‌ها طولانی‌تر خواهند بود.
  • وقتی اندازه تیم شما کوچک یا متوسط است و  تجربه تیم کمتر از استاندارد باشد، بهترین گزینه استفاده از monolith خواهد بود.
  • وقتی بودجه زیر ساخت شما محدود باشد. اگر چه در طول زمان شما با استفاده از میکروسرویس‌ها در هزینه صرفه‌جویی می‌کنید ولی باید بدانید که هزینه‌های شروع، خیلی بیشتر هستند.

از همه مهتر، هیج وقت میکروسرویس را به دلیل روی بورس بودن و استفاده آن در شرکت های بزرگ و یا پیشنهاد از یک آدم معروف انتخاب نکنید(در یک کلام جو نگیرتتون ). برای خیلی از کاربردها هنوز هم مونولیت‌ها راهکار خیلی خوبی هستند. شما می‌توانید با مونولیت شروع کنید و در ادامه در صورت نیاز آن را با میکروسرویس بشکنید. به یاد داشته باشید اکثر نام‌های بزرگ صنعت بعد از تجربه ۲ تا ۳ باز refactor کردن کد بیس‌های مونولیت، سراغ استفاده از میکروسرویس رفته‌اند.

کی باید سراغ معماری میکروسرویس برویم‌؟‌

به طور کلی، میکروسرویس‌ها می‌توانند وقتی مفید باشند که شما یکی از سناریوهای زیر را داشته باشید.

  • وقتی دومین‌ کار شما خیلی پیچیده باشد و تیم بزرگ و با تجربه‌ای دارید در این صورت تکه تکه کردن دومین به میکروسرویس‌ها، کار را برای شما آسان‌تر می‌کند.
  • وقتی شما انتظار دارید فیسبوک، نتفلیکس و یا توییتر بعدی، از لحاظ فشار کاربران باشید، به عبارت دیگر وقتی رشد نمایی کاربران را انتظار دارید.
  • اگر نرم افزار شما به عنوان SaaS قرار است تامین کننده API برای نرم افزارهای دیگری با تعداد کاربر بالا باشد. مانند درگاه‌های پرداخت، درگاه‌های ارسال پیام کوتاه و...
  • وقتی شما یک نرم‌افزار تجارت الکترونیک با تعداد کاربران خیلی بالا و با بار نا‌متعادل روی بخش‌های مختلف سیستم خود هستید، باید سراغ میکروسرویس‌ها بروید.( دفعه بعدی که یکی از فامیل ازتون پرسید یه سایت مثل آپارات و دیجیکالا میخوام و انقدر پول داشت و گیر بود که حتما خواست اینکار رو بکنه سراغ میکروسرویس نرو برادر من!)

نتیجه گیری

به طور کلی، هیچ وقت یک الگوی معماری را صرف اینکه در جایی کار کرده، انتخاب نکنید. الگویی را انتخاب کنید که برای use case و نیازهای scaling شما مناسب باشد. همه قرار نیست مثل آپارات میلیون‌ها درخواست هم‌زمان کاربران را مدیریت کنند و یا چندین ترابایت از اطلاعات را استریم کنند.