پیش زمینه
پیش از بررسی معماری میکروسرویس فرض کنید شما در حال توسعه یک برنامه Enterprise سمت سرور هستید. باید از انواع کلاینتهای مختلف از جمله مرورگرهای دسکتاپ، مرورگرهای موبایل و برنامههای کاربردی تلفن همراه Native پشتیبانی کنید. همچنین ممکن است یک API را برای ۳rd parties ایجاد کنید. همچنین ممکن است از طریقweb service ها یا message broker ها با سایر برنامه ها تعامل داشته باشید. برنامه درخواست ها (درخواست های HTTP و پیامها) را با اجرای business logic، دسترسی به پایگاه داده و یا تبادل پیام با سیستم های دیگر مدیریت میکند و یک پاسخ HTML/JSON/XML را برمی گرداند. اجزای منطقی مختلفی مربوط به حوزه های عملکردی برنامه وجود دارد.
مسئله
معماری استقرار برنامه در این وضعیت باید چگونه باشد؟
الزامات:
- تیمی از توسعه دهندگان در حال کار بر روی برنامه هستند
- اعضای جدید تیم باید به سرعت با تیم همگام شده و برنامه باید به راحتی برای آن ها قابل درک و اصلاح باشد
- شما می خواهید استقرار مداوم برنامه را مدیریت کنید
- شما باید چندین نمونه از برنامه را روی چندین ماشین اجرا کنید تا نیازهای مقیاس پذیری و در دسترس بودن را برآورده کنید.
- شما می خواهید از فناوری های نوظهور (فریم ورک ها، زبان های برنامه نویسی و غیره) استفاده کنید.
راه حل
شما باید معماری برنامه را به گونهای تعریف کنید که برنامه به مجموعهای از سرویسهای مشارکت کننده تقسیم شود که این سرویسها هم بصورت مستقل کارایی داشته باشند و هم در تعامل با همدیگر کارکرد کلی نرمافزار را بوجود آورند. به این نوع از مقیاس پذیری مقیاس پذیری محور Y میگویند. همچنین باید این موضوع را در نظر بگیرید که هر سرویس:
- کاملا قابل نگهداری و تست باشد. این ویژگی امکان استقرار سریع و مکرر را فراهم میکند.
- به صورت Loosely coupled با دیگر سرویسها در ارتباط باشد. یعنی وابستگی کم به دیگر سرویسها که این امر تیم توسعه را قادر میسازد تا اکثر اوقات به طور مستقل بر روی سرویس(های) خود کار کند، بدون اینکه تغییرات دیگر سرویسها بر روی سرویس(های) آنها و تغییرات آنها بر روی سرویس(های) خود بر روی دیگر سرویسها اثر بگذارد.
- به صورت مستقل امکان استقرار سرویس فراهم باشد. این ویژگی تیم توسعه دهنده را قادر میسازد تا سرویس خود را بدون نیاز به هماهنگی با سایر تیمها مستقر کند.
- قابلیت توسعه توسط یک تیم کوچک را داشته باشد. این ویژگی بهره وری را به شدت بالا میبرد. زیرا از سربار ارتباطات میان افراد تیمهای بزرگ اجتناب میکند.
در معماری میکرو سرویس، سرویسها در ارتباطات همزمان(Synchronous) از پروتکلهای همزمان مانند HTTP/REST و در ارتباطات غیرهمزمان(Asynchronous) از پروتکلهای غیر همزمان مانند AMQP استفاده میکنند. سرویسها همانطور که گفته شد میتوانند بصورت مستقل از یکدیگر توسعه یافته و مستقر شوند. هر سرویس پایگاه داده خود را دارد تا دادههای خود را از دیگر سرویسها جدا کند. سازگاری دادهها(Data Consistency) بین سرویسها با استفاده از الگوی Saga حفظ میشود.
برای کسب اطلاعات بیشتر در مورد ماهیت یک سرویس، لطفا این نوشته را مطالعه کنید.
مثال کاربردی
برنامه تجارت الکترونیک ساختگی
بیایید تصور کنیم که در حال ساخت یک برنامه تجارت الکترونیک(فروشگاه اینترنتی) هستید که سفارشات مشتریان را می گیرد، موجودی انبار و اعتبار کاربر را تأیید می کند و در نهایت سفارشات کاربران را ارسال می کند. این برنامه شامل چندین مؤلفه از جمله StoreFrontUI است که رابط کاربری را پیادهسازی میکند، همراه با برخی از سرویسهای backend برای بررسی اعتبار، نگهداری موجودی و سفارشهای ارسال. در نتیجه برنامه شامل مجموعه ای از سرویسها است.
فواید معماری میکروسرویس
این راه حل دارای چندین مزیت است:
- تحویل و استقرار مداوم برنامههای بزرگ و پیچیده را امکان پذیر میکند.
- قابلیت نگهداری بهبود یافته – هر سرویس نسبتا کوچک است و بنابراین درک و تغییر آن آسانتر است.
- آزمایش پذیری بهتر – سرویسها سادهتر و سریعتر آزمایش میشوند.
- قابلیت استقرار بهتر – سرویسها را میتوان به طور مستقل مستقر کرد. به شما این امکان را میدهد تا تلاشهای توسعه را در بین چندین تیم بصورت مستقل سازماندهی کنید. هر تیم میتواند سرویسهای خود را مستقل از سایر تیمها توسعه، آزمایش، استقرار و مقیاس بندی کند.
- هر میکرو سرویس نسبتا کوچک است که این امر، درک آن برای یک توسعه دهنده را آسان میکند و همچنین IDE سریعتر باعث بهرهوری توسعه دهندگان میشود. برنامه سریعتر شروع میشود، و سرعت اجرای آن نسبت به حالتی که تمام برنامه یکجا باشد بیشتر است.
- بهبود جداسازی خطا. به عنوان مثال، اگر نشت حافظه یا همان Memory Leak در یک سرویس وجود داشته باشد، تنها آن سرویس تحت تاثیر قرار میگیرد. در این وضعیت، سایر سرویسها به کارکرد عادی خود ادامه میدهند. چنین اتفاقی در یک برنامه با معماری یکپارچه(Monolithic) میتواند کل سیستم را از کار بیاندازد.
- هر گونه تعهد بلند مدت به استک تکنولوژی توسعه را حذف میکند. چرا که میتوان هنگام توسعه یک سرویس جدید، استک تکنولوژی را میتوان مستقل از سرویسهای دیگر انتخاب کرد. به طور مشابه، هنگام ایجاد تغییرات عمده در یک سرویس موجود، به دلیل ساده و کوچک بودن سرویسها، میتوان آن را با یک استک تکنولوژی جدید بازنویسی کرد.
مشکلات موجود در معماری میکروسرویس
- این راه حل در کنار مزایایی که دارد، تعدادی مشکل نیز به وجود میآورد که عبارتند از:
- توسعه دهندگان باید با پیچیدگی اضافی ایجاد یک سیستم توزیع شده(Distributed System)درگیر شوند.
- توسعه دهندگان باید مکانیزم ارتباط بین سرویسها را پیاده سازی کنند و احتمالا باید با خرابیهای جزئی که مربوط به نحوهی ارتباط بین سرویسهاست، مقابله کنند.
- اجرای درخواستهایی که چندین سرویس را در بر میگیرند دشوارتر است
- تست تعامل بین سرویسها دشوارتر است
- ابرارهای محیط توسعه،(IDE) بر روی ساخت برنامههای کاربردی یکپارچه متمرکز شدهاند و پشتیبانی صریحی برای توسعه برنامههای کاربردی توزیع شده ارائه نمیدهند
- پیچیدگی استقرار – در تولید یک سیستم که از سرویسهای بسیاری تشکیل شده است، عملیات استقرار نیز پیچیده خواهد بود.
- افزایش مصرف حافظه – معماری میکروسرویسی که N نمونه برنامه یکپارچه را به NxM نمونه از سرویسها تبدیل میکند، مثلا اگر زبان برنامه نویسی سرویسها به JVM یا معادل آن نیازمند باشد، سربار M برابر برای اجرای JVM های مجزا برای هر سرویس را دارد و این مورد برای ایزوله کردن نمونهها الزامی است. از طرفی معمولا هر سرویس بر روی ماشین مجازی مختص خود یا کانتینری مجزا اجرا میشود که ایجاد همین فضای اختصاصی میتواند سربار حافظه را باز هم افزایش دهد
مسائل معماری میکروسرویس
مسائل زیادی وجود دارد که باید به آنها رسیدگی کنید.
چه زمانی از باید معماری میکروسرویس استفاده کنیم؟
یکی از چالش های استفاده از این رویکرد این است که تصمیم بگیرید چه زمانی استفاده از آن منطقی است. هنگام توسعه اولین نسخه برنامه، اغلب مشکلاتی را که این روش حل می کند، ندارید. علاوه بر این، استفاده از یک معماری پیچیده و پراکنده، توسعه را کند می کند. استفاده از معماری میکرو سرویس در این وضعیت میتواند یک مشکل بزرگ برای استارتآپهایی باشد که بزرگترین چالش آنها اغلب چگونگی تکامل سریع مدل کسبوکار و برنامههای همراه است. با این حال، بعداً، هنگامی که چالش، چگونگی مقیاسسازی است و شما نیاز به استفاده از تجزیه عملکردی دارید، وابستگیهای درهمتنیده ممکن است تجزیه برنامه یکپارچه شما به مجموعهای از سرویسها را دشوار کند.
در معماری میکروسرویس چگونه اپلیکیشن را به سرویس ها تجزیه کنیم؟
چالش دیگر، تصمیم گیری در مورد نحوهی تقسیم سیستم به میکروسرویس ها است. این یک هنر بسیار بزرگ است، اما تعدادی استراتژی وجود دارد که می تواند در این امر به شما کمک کند:
-
- تجزیه بر اساس قابلیت تجاری و تعریف خدمات مربوط به قابلیت های تجاری.
- تجزیه بر اساس زیر دامنه طراحی Domain-driven
- تجزیه بر اساس فعل یا مورد استفاده و تعریف سرویسهایی که مسئول اعمال خاصی هستند. به عنوان مثال، یک سرویس حمل و نقل که مسئولیت ارسال سفارشات کامل را بر عهده دارد. با تعریف سرویسی که مسئول تمام عملیات موجودات/منابع از یک نوع معین است، توسط اسامی یا منابع تجزیه می شود. به عنوان مثال، یک سرویس حساب که مسئول مدیریت حساب های کاربری است.
- در حالت ایده آل، هر سرویس باید تنها مجموعه کوچکی از مسئولیت ها را داشته باشد. (عمو) باب مارتین در مورد طراحی کلاس ها با استفاده از اصل مسئولیت واحد (SRP یا همان Single Responsibility Principle) میگوید: مسئولیت یک کلاس به عنوان دلیلی برای تغییر آن تعریف می شود و بیان می کند که یک کلاس فقط باید یک دلیل برای تغییر داشته باشد. منطقی است که SRP را در طراحی سرویس نیز اعمال کنیم.
- قیاس دیگری که به طراحی سرویس کمک می کند، طراحی ابزارهای یونیکس است. یونیکس تعداد زیادی ابزار کاربردی مانند grep، cat و find را ارائه می دهد. هر ابزار دقیقاً یک کار را انجام می دهد، اغلب به طور استثنایی، این ابزار مجزا با استفاده از یک اسکریپت پوسته، برای انجام کارهای پیچیده ترکیب شوند.
در معماری میکروسرویس چگونه یکپارچگی داده ها را حفظ کنیم؟
به منظور اطمینان از loose coupling، هر سرویس پایگاه داده خاص خود را دارد. در این وضعیت حفظ سازگاری دادهها بین سرویسها یک چالش است. برای حل این چالش برنامه باید از الگوی Saga استفاده کند. یک سرویس زمانی یک رویداد را منتشر میکند که دادههای آن تغییر کند. سایر سرویسها آن رویداد را Consume میکنند. یعنی گوش به زنگ آن رویداد هستند تا در زمان وقوع، متناسب با آن دادههای خود را بروز کنند. راههای مختلفی برای بروز رسانی قابل اعتماد دادهها و انتشار رویدادها وجود دارد. از میان این راهها میتوان به Event Sourcing و Transaction log Tailing اشاره کرد.
چگونه کوئری ها را پیاده سازی کنیم؟
چالش دیگر پیاده سازی پرس و جوهایی است که نیاز به بازیابی داده های متعلق به چندین سرویس دارند. این چالش توسط روشهای API Composition و Command Query Responsibility Segregation(CQRS) قابل حل است.