در دو پست قبلی بحث معماری میکروسرویس را شروع کردیم. ابتدا با مثالی از یک اپلیکیشن با معماری یکپارچه زدیم و قدم به قدم مشکلاتش را بررسی کردیم. اصلیترین دغدغه ای که برای یک اپلیکیشن بزرگ داریم، گسترشپذیری است. در این پست به کمک scale cube با این مفهوم آشنا میشویم.
اگر دو پست قبلی را نخواندهاید، لطفا اول سری به آنها بزنید :)
معماری میکروسرویس - فرار از جهنم یکپارچه!
معماری میکروسرویس - مزایا و معایب یکپارچگی
و حال ادامه داستان...
در قسمتهای قبل دیدیم که ماری CTO شرکت FTGO است و او نهایتا به این نتیجه رسید که معماری میکروسرویس برای FTGO انتخاب مناسبی است.
از آن جاییکه معماری ارتباط کمی با نیازمندی ها دارد، هر مجموعه از یوزکیسها را میتوان با هر معماری پیادهسازی کرد. (منظور از یوزکیس به طور ساده نیازمندیهای کارکردی نرمافزار است.)
در واقع معماری یک نرمافزار روی نیازمندیهای غیرکارکردی یا به عبارتی روی نیازمندیهای مربوط به کیفیت و در اصطلاح quality attribute تاثیرگذار است.
با بزرگ شدن اپلیکیشن FTGO تعدادی از quality attribute ها مانند قابلیت نگهداری، گسترشپذیری و آزمونپذیری تحت تاثیر قرار گرفته و افت کردند.
هر چقدر هم که تیم توسعه خبره باشند و ماژولاریتی را رعایت کند و تستهای خودکار بنویسد، باز هم نمیتواند از پس معضل بزرگ شدن تیم توسعه و کار کردن روی تنها یک فایل اجرایی برآید و تنها میتواند مشکلات را به تاخیر اندازد اما نمیتواند جلوی رخ دادنشان را بگیرد.
امروزه عقیده اکثر افراد بر این است که اگر یک نرمافزار بزرگ و پیچیده دارید، از معماری میکروسرویس استفاده کنید. اما سوالی که پیش میآید این است که معماری میکروسرویس دقیقا چیست؟
تعاریف گوناگونی برای این مفهوم وجود دارد، بعضی معتقدند میکروسرویس همانطور که از نامش پیداست، مجموعه چند سرویس کوچک است برای مثال هر سرویس نهایتا 100 خط کد دارد. برخی دیگر میگویند که سرویس باید در یک زمان کوتاه برای مثال دو هفته توسعه پیدا کند. Adrian Cockcroft که قبلا در نتفلیکس کار میکرد معتقد است معماری میکروسرویس نوعی معماری سرویسگرا است که از المانهای loosely coupled که bounded context دارند، تشکیل شده است. در ادامه این تعریف را واکاوی میکنیم تا بفهمیم دقیقا به چه معناست؟
برای درک بهتر تعریف بالا، ابتدا با scale cube آشنا میشویم:
در کتاب The Art of Scalability نوشته Martin Abbott و Michael Fisher یک مدل گسترشپذیری جالب معرفی شده است:
این مکعب سه روش مختلف برای گسترش پذیری یک اپلیکیشن را بیان میکند:
روش X-axis
گسترش پذیری X-axis یکی از روشهای رایج برای scale کردن یک اپلیکیشن monolithic است. در این حالت نمونههای مختلف اپلیکیشن اجرا شده و جلوی اینها یک لود بالانسر قرار میگیرد. لودبالانسر درخواستها را بین این نمونههای مختلف توزیع میکند و این روش، راهی عالی برای بهبود ظرفیت و در دسترس بودن یک اپلیکیشن است. (برای مثال اگر یک نمونه اجرایی به مشکل بخورد، درخواستها سمت آن نمیروند و نمونههای دیگر فعال هستند. در نتیجه اپلیکیشن کماکان در دسترس خواهد بود.)
شکل زیر بیانگر این نوع گسترشپذیری است:
روش Z-axis
در گسترشپذیری به روش Z-axis، چندین نمونه از اپلیکیشن monolithic اجرا میشوند. اما برخلاف X-axis، هر نمونه تنها مسئول زیرمجموعهای از داده است. مطابق آن چه در شکل زیر دیده میشود، یک router جلوی نمونههای مختلف اپلیکیشن قرار گرفته است و درخواستها را مسیریابی میکند. این کار میتواند براساس پارامتری مانند userId انجام شود.
گسترشپذیری به روش Z-axis برای scale کردن اپلیکیشنی که قرار است تراکنشها و حجم داده زیاد و روزافزون هندل کند، عالی است.
روش Y-axis
هر دو روش X-axis و Z-axis، ظرفیت و در دسترس بودن اپلیکیشن را بهبود میبخشند اما هیچ کدام نمیتوانند مساله توسعه و پیچیدگی را حل کنند. برای حل این مورد از Y-axis یا functional decomposition استفاده میشود. همانطور که در شکل زیر دیده میشود، این روش از طریق تقسیم اپلیکیشن monolithic به مجموعهای از سرویسها کار میکند.
یک سرویس یک برنامه کوچک است که با تمرکز روی کارکرد خاصی پیادهسازی میشود. مانند مدیریت سفارشات، مدیریت مشتری و ... (یادآوری: اپلیکیشن FTGO که در این کتاب بررسی میشود، یک اپلیکیشن سفارش غذای آنلاین است.) حال هر سرویس میتواند به تنهایی با روش X-axis یا Z-axis، scale شود. برای مثال سرویس Order در بالا، از یک لودبالانسر و نمونههای مختلف سرویس استفاده کرده است.
یک تعریف سطح بالا از معماری میکروسرویس عبارت است از یک استایل معماری که از نظر کارکردی یک اپلیکیشن را به مجموعهای از سرویسها تقسیم میکند. دقت کنید که این تعریف هیچ چیزی درباره سایز سرویس نمیگوید. به جای آن چیزی که اهمیت دارد این است که هر سرویس یک مجموعه وظایف منسجم دارد و روی آنها متمرکز است.
معماری میکروسرویس یک نوع modularity است
ماژولاریتی یک مفهوم ضروری به هنگام توسعه یک اپلیکیشن بزرگ و پیچیده است. یک اپلیکیشن امروزی مانند FTGO بزرگتر از آن است که توسط یک فرد توسعه یابد. همچنین پیچیدهتر از آن است که توسط یک فرد فهمیده شود. بنابراین این اپلکیشن نیاز است به چندین ماژول تقسیم شود و هر ماژول توسط گروهی توسعه یابد. با یک معماری یکپارچه با توجه به دو پست قبلی، هر چقدر هم که ماژولاریتی رعایت شود، در عمل به big balls of mud تبدیل میشود.
در معماری میکروسرویس از سرویسها به عنوان یونیتهای ماژولاریتی استفاده میشود. هر سرویس یک API دارد که مرزی بین سرویسها تعیین میکند و اجازه نمیدهد یک سرویس به کلاس داخلی سرویس دیگر دسترسی داشته باشد. بنابراین به مرور زمان ماژولاریتی به خوبی تضمین میشود و از طرفی سرویسها به عنوان building block ها هستند و میتوانند مستقیما دیپلوی یا scale شوند.
هر سرویس پایگاهداده خودش را دارد
یکی از مشخصههای بارز میکروسرویس این است که سرویسها کاملا loosely coupled هستند و ارتباط آنها تنها از طریق API میباشد. یکی از روشهای دستیابی به loosely coupled این است که هر سرویس پایگاهداده خودش را داشته باشد. بنابراین در هر سرویس میتوان به راحتی schema پایگاهداده را عوض کرد بدون آن که نگران باشیم در کار سرویس دیگری اختلال ایجاد میشود. همچنین در زمان اجرا، هر سرویس در محیطی کاملا ایزوله قرار دارد، بنابراین نگران آن نیستیم که یک سرویس در دسترسی به پایگاهداده بلاک شود چون سرویس دیگری با پایگاهداده کار میکند.
در نوشته بعدی، برای معماری یکپارچه FTGO که قبلا دیدیم، معماری میکروسرویس ارائه میکنیم و این معماری را با معماری سرویسگرا مقایسه کرده و مزایا و معایب میکروسرویس را بیان میکنیم.
منبع:
Microservices Patterns: With Examples in Java Book by Chris Richardson