مقدمه ای بر معماری میکرو سرویس (قسمت اول)

امروزه مقالات، وبلاگ ها، رسانه های اجتماعی ، سخنرانی های و کنفرانس ها توجه زیادی به معماری میکرو سرویس می کنند. در عین حال، در جامعه نرم افزاری شک و تردید وجود دارد که آیا میکروسرویس بحث جدیدی است . Naysayers ادعا می کنند که این ایده فقط بازنویسی SOA است. با این حال، علیرغم این شک و تردید ها، الگوی معماری Microservices دارای مزایای قابل توجهی است، به ویژه هنگامی که این مزایا سبب توسعه سریع و تحویل برنامه های کاربردی پیچیده سازمانی می گردد. در ابتدا به معماری Monolithic نرم افزارها نگاهی می اندازیم.تصور کنید که شما شروع به ساخت یک برنامه تاکسی جدید برای رقابت با Uber می کنید . پس از جلسات اولیه و جمع آوری الزامات، شما می توانید یک پروژه جدید را به صورت دستی یا با استفاده از یک کد Generator ایجاد کنید. این نرم افزار جدید یک معماری شش ضلعی ( hexagonal ) مدولار دارد، مانند نمودار زیر :در هسته برنامه منطق کسب و کار توسط ماژول هایی تعریف شده که سرویس ها، اشیاء دامنه و رویدادها آن ها را پیاده سازی کرده اند.در اطراف هسته برنامه، آداپتورهایی هستند که با دنیای خارج ارتباط برقرار می کنند. نمونه هایی از آداپتورها شامل اجزای دسترسی به پایگاه داده، اجزای پیام رسانی که پیام ها را تولید و مصرف می کنند و اجزای وب کهAPI ها را ازائه می دهند یا UI را پیاده سازی می کنند.اگر به تصویر بالا نگاه کنیم با اینکه یک معماری منطقی مدولارمشاهده می شود ولیکن برنامه در داخل یکPackage قرار گرفته و به صورت یکپارچه به کار می رود.نکته دیگر اینکه اجزا معماری فوق همگی در قالب یک زبان و فریمورک قرار دارند . برای مثال بسیاری از برنامه های سمت سرور جاوا در قالب فایل های WAR ، Package می شوند و در سرورهای قرار می گیرند. برنامه های پیاده سازی شده براساس Rails و Node.js براساس سلسله مراتبی از دایرکتوری ها Package می شوند ویا در .NET این امر درقالب فایل های dll اتفاق می افتد.پیاده سازی برنامه به این نحو امری رایج است . توسعه این مدل نیز راحت است چراکه در یک ابزار (IDE) می توان کار توسعه و تست را انجام داد . همچنین deploy نرم افزارهای Monolithic کار آسانی است شما با کپی کردن فایل های در سرور براحتی این کاررا انجام می دهید اگرهم نیاز به load balancing داشته باشیم کافیست فایل ها را در سرورهای متعدد کپی کردن و اجرا نمایید.متأسفانه این رویکرد ساده محدودیت بزرگی دارد. نرم افزارهای موفق در طول زمان رشد می کنند و بزرگ می شوند. تیم توسعه در هر اسپرینت چند Story را پیاده سازی میکند که این به معنا افزوده شدن تعداد زیادی خط کد می شود درنهایت پس ازچند سال ، برنامه کوچک و ساده ما تبدیل به یک هیولای Monolithic می شود . وقتی نرم افزار شما بزرگ شد و به یک Monolithic پیچیده تبدیل شد سازمان دهی توسعه نرم افزار ما دچاریک فاجعه می شود. هرگونه تلاش در جهت توسعه و تحویل چابک محصول شکست می خورد. یکی از مشکلات عمده این است که برنامه به طور کامل پیچیده است . رفع مشکلات نرم افزار و پیاده سازی قابلیت های جدید بسیار مشکل و با هزینه زمانی بالا امکان پذیر است بعلاوه با توجه به پیچیده گی هایی که در کد وجود دارد درک صحیح کد و تغییرات درست در آن نیز دارای ریسک بالایی خواهد بوداز دگر سو هرچه سایز برنامه بزرگتر باشد توسعه در آن کندتر می شود. برای نمونه زمان start‑up یک برنامه بزرگ زیاد است در یک نظر سنجی که اخیرا انجام شده است این زمان بیشتر از 12 دقیقه اعلام شده است اگر توسعه دهندگان به طور مرتب سرور را مجددا راه اندازي کنند، بخش بزرگی از روز خود را صرف اینکار خواهند کرد که با عث کاهش بهره وری انها می شودیکی دیگر از مشکلات یک برنامه یکپارچه و پیچیده، این است که مانع از استقرار مداوم (continuous deployment) است. امروزه برای برنامه های SaaS می بایست چندین بار در روز تغییرات push شود. این یک مشکل پیچیده است زیرا شما باید کل برنامه را دوباره redeploy تا یک بخش از آن را به روزرسانی کنید. از منظردیگر همانطور که بالاتر اشاره شد زمانهای طولانی راه اندازی نیز به این مسئله افزوده می شوند. در نتیجه، استقرار مداوم غیرممکن است.زمانی که ماژول های مختلف ، منابع متناقضی نیازمند هستند برنامه های یکپارچه دچار مشکل می شوند به عنوان مثال، یک ماژول می تواند منطق پردازش تصویر را اجرا کند ونیازمند پردازنده خاصی باشد. یکی دیگر از ماژول ها ممکن است نیازمند یک پایگاه داده in‑memory باشد که برای این نیاز سخت افزار بهینه سازی شده با حافظه مناسب نیاز است. حال، به دلیل اینکه این ماژول ها به هم متصل می شوند (با توجه به معماری Monolithic )، می بایست در انتخاب سخت افزار مصالحه کنیم.مشکل دیگر با برنامه های یکپارچه reliability است. از آنجا که تمام ماژول ها در یک process در حال اجرا هستند، یک اشکال در هر ماژول، مانند نشت حافظه، می تواند باعث شود کل process مختل شود.نهایتا اینکه برنامه های Monolithic در سازگاری با زبان ها و فریمورک های جدید هزینه های زیادی دارند.برای مثال تصور کنید که شما 2 میلیون خط کد را با استفاده از چارچوب XYZ نوشته اید. بازنویسی کل برنامه برای استفاده از چارچوب ABC جدیدتر بسیار گران (زمان و هزینه) است ، حتی اگر این چارچوب به مراتب بهتر باشد. در نتیجه، یک مانع بزرگ برای استفاده از فن آوری های جدید وجود دارد. شما با انتخاب تکنولوژی که در ابتدای پروژه انجام داده اید تا انتها درگیر خواهید بود.بسیاری از سازمانها مانند آمازون، eBay و Netflix این مشکل را با اتخاذ آنچه که اکنون به عنوان معماری Microservices شناخته می شوند، حل کرده اند. به جای ایجاد یک برنامه ی یکنواخت ویکپارچه، برنامه خود را بصورت مجموعه ای از سرویس های کوچکتر متصل پیاده سازی کردند .سرویس معمولا مجموعه ای از ویژگی ها یا قابلیت های متفاوتی مانند مدیریت سفارش، مدیریت مشتری و غیره را پیاده سازی می کند. هر میکرو سرویس یک برنامه کوچک است که معماری شش ضلعی خود را دارد که شامل منطق تجاری و آداپتورهای مختلف می باشد. برخی از میکرو سرویس یک API را که توسط سایر میکرو سرویس یا مشتریان برنامه مورد استفاده قرار می گیرد، ارائه می دهد. سایر میکرو سرویس ها ممکن است نیازمندی های UI وب را پیاده سازی کنند.به عنوان مثال، معماری Microservices سیستمی که قبلا شرح داده شده است در نمودار زیر نشان داده شده است:هر حوزه کاربردی از برنامه در حال حاضر توسط میکرو سرویس خود پیاده سازی می شود. علاوه بر این، برنامه وب به مجموعه ای از برنامه های کاربردی وب ساده تر (مانند یک برای مسافران و یکی برای رانندگان در مثال ما) تقسیم می شود. این باعث می شود که تجربیات متمایز Deploy برای کاربران خاص، دستگاه ها یا use case های خاص تسهیل گردد. هر سرویس backend توسط یک REST API ارائه می شود و اکثر سرویس ها API های ارائه شده توسط سرویس های دیگر را استفاده می کنند.به عنوان مثال، Driver Management از سرور Notification استفاده می کند تا یک راننده را در مورد سفر احتمالی مطلع کند. سرویس ها همچنین ممکن است از ارتباطات asynchronous مبتنی بر پیام استفاده کنند. برخی از API REST ها نیز به برنامه های تلفن همراه مورد استفاده رانندگان و مسافران سرویس ارائه می کنند با این حال، برنامه ها دسترسی مستقیم به backend service ها را ندارند . در عوض، ارتباطات توسط یک واسطه شناخته شده به عنوان API Gateway برقرار می شوند. API Gateway مسئولیت وظایفی مانند load balancing، caching، کنترل دسترسی، API metering و monitoring است.در شکل بالا که برگرفته از کتاب "هنر مقیاس پذیری" است یک مدل سه بعدی مقیاس پذیری را ارائه می دهد که محور Y مطابق با مقیاس پذیری مبتنی بر الگوی معماری Microservices می باشد محور X نمایانگر تعداد نسخه های یکسان اجرا شده از یک برنامه است که می تواند با مکانیزم Load Balancing اتفاق بیفتدو محور Z (یا پارتیشن بندی داده ها)، که در آن یک ویژگی درخواست (به عنوان مثال، کلید اولیه از یک ردیف یا هویت یک مشتری برای route درخواست به یک سرور خاص استفاده می شود. نمودار زیر نشان می دهد که چگونه سرویس Trip Management را می توان بر Container های مبتنی بر Docker در Amazon EC2 ،مستقر نمود.در زمان اجرا، نمونه های متعدد سرویس Trip Management ایجاد می گردد. هر نمونه از سرویس یک Container Docker است. برای اینکه highly available بالاتر رود، Container ها در چند Cloud VM اجرا می شوند. در جلوی نمونه های سرویس ها یک سرور Load Balancer از قبیل NGINX است که درخواستها را در بین نمونه های مختلف توزیع می کند. load balancer همچنین ممکن است نگرانی های دیگر مانند ذخیره سازی، کنترل دسترسی، اندازه گیری API و نظارت را کنترل کند.الگوی معماری Microservices به طور قابل ملاحظه ای ارتباط بین برنامه و پایگاه داده را تحت تاثیر قرار می دهد. به جای به اشتراک گذاشتن یک پایگاه داده یکپارچه برای همه سرویس ها ، هر سرویس دارای پایگاه داده خود است. از یک طرف، این رویکرد با ایده مدل داده های سازمانی سازگار است ولیکن تکرار برخی از داده ها را به دنبال دارد. با این حال، داشتن یک پایگاه داده برای هر سرویس ضروری است، زیرا این مورد loose coupling را تضمین می کند. نمودار زیر معماری پایگاه داده برای برنامه مثال ما را نشان می دهد.هر یک از سرویس ها پایگاه داده خود را دارد. علاوه بر این، یک سرویس می تواند از یک نوع پایگاه داده ای استفاده کند که برای نیازهایش مناسب تراست، که به این معماری داده ای اصطلاحا معماری ذخیره سازی polyglot می گویند. به عنوان مثال، مدیریت رانندگی، که راننده های نزدیک به یک مسافر بالقوه را پیدا می کند، باید از پایگاه داده ای که از پرسوجوی های geo (مختصات جغرافیایی) کارآمد پشتیبانی می کند استفاده نماید.مزایای Microserviceالگوی معماری Microservices دارای مزایای مهمی است. اول، این معماری به مشکل پیچیدگی می پردازد. این معماری تجزیه مسئله به مجموعه ای از سرویس ها را به جای تولید یک برنامه یکپارچه هیولا پیشنهاد می کند. در حالی که میزان کلی عملکرد برنامه بدون تغییر می ماند، برنامه به تکه های مدیریت شونده یا همان سرویس ها شکسته شده است. هر سرویس دارای یک مرز تعریف شده می باشد که در قالب یک API RPC یا مکانیزم پیام محور به مشتریان سرویس می دهد. الگوی معماری Microservices یک سطح مدولار را پیاده سازی می کند که در عمل اگر برپایه معماری monolithic پیاده سازی شود بسیار دشوار است. در نتیجه، سرویس های تکی بسیار سریعتر توسعه می یابند و بسیار ساده تر درک و نگهداری می شوند.دوم، دراین معماری، هر تیم به طور مستقل بر یک سرویس متمرکز است و به طور مستقل آن را توسعه می دهد. توسعه دهندگان می توانند هر گونه فناوری را انتخاب کنند، البته، اکثر سازمان ها از هرج و مرج دوری می کنند و گزینه های تکنولوژی محدود استفاده می کنند با این حال، این آزادی بدان معنی است که توسعه دهندگان دیگر مجبور نیستند از فناوری های که در آغاز یک پروژه جدید وجود داشته استفاده کنند. هنگام نوشتن یک سرویس جدید، آنها می توانند از تکنولوژی فعلی استفاده کنند. علاوه بر این، از آنجا که سرویس ها نسبتا کوچک هستند، امکان بازنویسی مجدد سرویس های قدیمی با استفاده از تکنولوژی جدید امکانپذیر است.سوم، با الگوی معماری Microservices، هر سرویس می تواند بصورت مستقل deploy شود. توسعه دهندگان هرگز نیازی به هماهنگ کردن تغییراتی که محلی هستند جهت deploy سرویس خود نیستند. این نوع تغییرات می تواند به محض آزمایش اعمال شود. برای مثال تیم UI می تواند تست A / B را انجام دهد و به سرعت تغییرات UI را انجام دهد. الگوی معماری Microservices امکان استقرار مداوم (continuous deployment ) را فراهم می کند.در نهایت، با الگوی معماری Microservices هر سرویس بصورت مستقل قابلیت مقیاس پذیری دارد. شما می توانید به تعداد مورد نیاز از هر نمونه سرویس deploy نمایید، تا جایی که ظرفیت و قابلیت دسترس پذیری مورد نظر برای هر یک ازآنها برآورده شود. علاوه بر این، شما می توانید برای هر سرویس بسته به نیازی که دارد از مناسب ترین سخت افزار استفاده کنید.معایب Microservicesمعماری Microservices مانند هر تکنولوژی دیگر دارای اشکالاتی است. یکی از این اشکالات نام خودش است. اصطلاح microservice تاکید بیش از حد بر اندازه سرویس است. برخی از توسعه دهندگان وجود دارند که بسیار خوب سرویس هایی در سایز 10-100 LOC می نویسند. سرویس های کوچک ترجیح داده می شوند، اما نکته مهم که باید به یاد داشته باشید اینست که آنها یک وسیله برای رسیدن به هدف هستند نه هدف اصلی. هدف از microservice این است که برنامه را به اندازه کافی تجزیه کنید تا توسعه و استقرار چابک برنامه ها تسهیل شود.یکی دیگر از اشکالات دیگر این معماری اینست که برنامه کاربردی مبتنی بر این معماری درواقع یک سیستم توزیع شده با پیچیدگی های مرسوم است . توسعه دهندگان باید مکانیزم ارتباطی بین سرویس ها را بر اساس پیام یا RPC انتخاب و پیاده سازی کنند. علاوه بر این، آنها باید کدهایی جهت handle کردن failure هایی که ممکن است برای عدم دسترس پذیری مقصد یا کندی مقصد اتفاق می افتد پیاده سازی کنند. این فرآیند بسیار پیچیده تر از یک برنامه مبتنی بر معماری یکپارچه است که ماژول ها با استفاده از روش های فراخوانی در سطح زبان (method/procedure calls)، با یکدیگر ارتباط دارند.یکی دیگر از چالش های microservice معماری پایگاه داده تقسیم شده می باشد. امروزه تراکنش های کسب و کاری که موجودیت های کسب وکاری متعدد را به روزرسانی می کنند نسبتا رایج هستند. پیاده سازی این نوع تراکنش ها در یک برنامه ی یکپارچه امری نسبتا ساده ای است زیرا یک پایگاه داده واحد وجود دارد در حالیکه در یک برنامه مبتنی بر microservices، شما باید چندین پایگاه داده متعلق به سرویس های مختلف را به روز کنید. استفاده از تراکنش توزیع شده در microservices مشکلات دیگری نیز دارد، آنها به سادگی توسط پایگاه های داده NoSQL و message broker ها پشتیبانی نمی شوند و این برای برنامه نویسان چالش آور است.تست نرم افزار microservices نیز بسیار پیچیده است. برای مثال، با یک چارچوب مدرن مانند Spring Boot، می توان یک کلاس تست برای یک برنامه وب یکپارچه نوشت واتوماتیک تست را راه اندازی کرد و API REST آن را تست کرد. در مقابل، یک کلاس تست مشابه برای یک سرویس نیاز به راه اندازی این سرویس و هر سرویس که وابسته به آن است (یا حداقل راه اندازی Mock آنها) می باشد که اینکار پیچیدگی های تست را افزایش می دهد.یکی دیگر از چالش های عمده با الگوی معماری Microservices تاثیر تغییرات چندین سرویس را دربرمی گیرد. به عنوان مثال، بیایید تصور کنیم که شما یک Story را پیاده سازی می کنید که نیاز به تغییرات در سرویس های A، B و C دارد وA بستگی به B دارد و B به C بستگی دارد. در یک برنامه ی Monolithic ، شما می توانید به سادگی ماژول های مربوطه را تغییر دهید، و تغییرات را بصورت یکپارچه اعمال نمایید. در مقابل، در الگوی معماری Microservices شما باید با دقت تغییرات هر یک از سرویس ها را برنامه ریزی و هماهنگ نمایید. درمثال بالا، شما برای اعمال به روز رسانی می بایست به ترتیب سرویس C، و سپس سرویس B، و در نهایت سرویس A را بروزرسانی نمایید .خوشبختانه، اغلب تغییرات معمولا فقط یک سرویس را تحت تاثیر قرار می دهند و تغییرات چند سرویسی که نیاز به هماهنگی دارند نسبتا نادر است....................