مرضیه برخی، مهنوش شکری
میکروسرویسها سبکی از معماری است که روی تقسیم سیستم به سرویسهای کوچکی که برای انجام یک عملکرد تجاری منسجم ساخته شده اند، تاکید دارد. این معماری تکاملی از سبک معماری سنتی سرویسگرا میباشد.
از مزایای معماری میکروسرویس میتوان به افزایش چابکی، بهرهوری، انعطافپذیری، مقیاسپذیری، قابلیت اطمینان، قابلیت نگهداری، جداسازی دغدغهها و سهولت استقرار اشاره کرد.
شرکتهای مشاوره نرمافزار و طراح محصول بر این باورند که معماری میکروسرویس به تیمها و سازمانهای نرمافزاری این امکان را میدهد که بهرهوری بالاتری داشته باشند و محصولات موفقتری بسازند. همچنین میکروسرویسها معماری مناسبی برای سیستمهای ابری هستند. شرکتهایی مانند Netflix، Amazon و SoundCloud از معماری میکروسرویس در فضای ابری استفاده میکنند.
در این سبک معماری با چالشهایی مانند یافتن سرویسها از طریق شبکه، مدیریت امنیت، بهینهسازی ارتباطات، اشتراکگذاری دادهها و کارایی روبرو هستیم.
در این پست به بررسی معماری میکروسرویسها و روند پیشرفت آن، فناوریهای مرتبط با میکروسرویسها و ابزارهای ارائه شده برای آنها، چالشهای موجود در میکروسرویسها و ضدالگوهای شناخته شده در این معماری میپردازیم. همچنین در ادامه معماری سه شرکت Netflix، Amazon و SoundCloud را به عنوان نمونههای کسبوکارهای موفقی که از معماری میکروسرویسها استفاده میکنند مورد بررسی قرار میدهیم.
میکروسرویسها، با توجه به سرویسهای مستقل با مرزهای مشخص، مشابه معماری قدیمیتر سرویسگرا (SOA) میباشند. در واقع میتوان ادعا کرد که معماری میکروسرویس نوع خاصی از معماری سرویسگراست.
با مقایسه میکروسرویسها و معماری سرویسگرا میتوانیم تفاوتهای زیر را برای آنها برشمریم:
بعضی از خصوصیات مشترکی که انتظار میرود در سیستمهایی که با معماری میکروسرویسها توسعه داده شده اند وجود داشته باشد به شرح زیر است:
وجود سرویسها به عنوان مولفه پیرامون کارکردهای کسبوکار: مؤلفهها (componentها) در یک سیستم نرمافزاری، واحدهایی هستند که میتوانند بدون این که در کار سایر بخشهای سیستم ایرادی وارد شود، به طور مستقل اجرا، جایگزین و بروزرسانی شوند. شکستن سیستم به میکروسرویسها باید بر مبنای کارکردهای کسبوکار (و نه از نظر فنی) باشد. هر تیمی که مسئولیت میکروسرویسی را بر عهده گرفته است، باید شامل متخصصانی از تمامی حوزههای فنی باشد تا بتوانند به طور مستقل آن میکروسرویس را توسعه داده و نگهداری کنند.
تمرکز بر محصول، نه پروژه: در میکروسرویسها تمرکز بر این است که بتوان در بازههای زمانی کوتاهتری، محصولی قابل ارائه را عرضه کرد و پس از عرضه نیز باید با کاربر در تعامل بود تا در صورت نیاز، نیازمندیها بهبود پیدا کند و یا مشکلات حل شود. واضح است که در سیستم monolith به دلیل یکپارچگی و وابستگی اجزای مختلف سیستم با هم، امکان تغییر، بهبود و یا رفع bug به آسانی وجود ندارد.
طراحی هوشمندانه endpointها: همانطور که در معماری سرویسگرا امکان ارتباط و فرستادن پیام بین سرویسها، به واسطه یک گذرگاه سازمانی مثل ESB مطرح است، در معماری میکروسرویس از گذرگاههای سبکوزنی مثل message brokerها برای انتقال پیام به صورت غیرهمزمان استفاده میشود که به عنوان واسطی بین مبدا و مقصد برای دریافت و انتقال پیامها عمل میکند. معمولا هر endpoint در اجزای معماری میکروسرویس با پروتکلهای REST به نقطه مقابل خود متصل میشود. موردی که مطرح است این است که برخلاف معماری سرویسگرا که در آن پروتکلهای ارتباطی میتوانستند از هر نوعی باشند، در معماری میکروسرویس این استاندارد پذیرفته شده است که پروتکلها، غالبا REST باشند، پس Broker که یک endpoint در این سیستم است، به سنگینی ESB نیست که بخواهد عمل تبدیل انواع قالبهای پیام را به یکدیگر انجام دهد؛ بلکه صرفا به عنوان یک صف پیام عمل میکند.
مدیریت غیرمتمرکز : از آنجایی که در میکروسرویسها اصل بر این است که میتوان بنابر نیاز از هر زبان برنامهسازی استفاده کرد و هر میکروسرویس واحد مستقلی است، دیگر الزامی برای مدیریت متمرکز مؤلفهها وجود ندارد و صرفا باید ارتباطات بین آنها مدیریت شود. اما این خودمختاری در توسعهی میکروسرویسها به این معنا نیست که همیشه بدون هیچ دورنمایی، میتوان تکنولوژیها را انتخاب کرد؛ بلکه این انتخاب باید بر مبنای معیارهایی چون دانش فنی اعضای تیم، قابلیت نگهداری سیستم و ملاحظاتی از این دست باشد.
مدیریت داده غیرمتمرکز: یکی از راهحلهای جداسازی مدیریت دادهها در مؤلفههای مختلف سیستم، طراحی دامنه محور (DDD) است. در این روش طراحی، یک دامنه بزرگ به تعدادی زیردامنه محدود شکسته میشود و ارتباط بین هر یک از آنها با نگاشت انجام میشود. در میکروسرویس ترجیح بر این است که هر سرویس که بخشی از نیاز کسبوکار را مرتفع میکند، دیتابیس مختص خود را داشته باشد و یا حتی اگر از دیتابیسهای مشترکی استفاده میشود، از instanceهای متفاوتی برای اتصال به آن استفاده کند. به این روش polyglot persistence هم گفته میشود.
خودکارسازی زیرساخت: پیشرفت بستر Cloud و روی کارآمدن AWS باعث شده است که پیچیدگیهای ساخت، استقرار و عملیاتی کردن میکروسرویسها کاهش یابد. اغلب محصولات یا سیستمهایی که با معماری میکروسرویس توسعه داده شدهاند، اصل CI/CD را رعایت میکنند و زمینه اجرای این اصل، خودکارسازی فرآیندهای توسعه، تست و استقرار است.
طراحی مکانیزم مدیریت خطا: از آن جایی که در این معماری سرویسها باید با یکدیگر ارتباط برقرار کنند، لازم است مکانیزمی برای تحمل خطا برای هر سرویسی که با سرویس دیگر در ارتباط است وجود داشته باشد. هر درخواستی که از یک سرویس به سرویس دیگری ارسال میشود، ممکن است به دلیل در دسترس نبودن یا خرابی سرویس مقصد، بدون پاسخ بماند. پس باید تکلیف این درخواستها مشخص باشد و به اصطاح مانا باشند. این مورد یکی از معایب استفاده از معماری میکروسرویس در برابر معماری monolithic است. چون ممکن است سرویسها در هر لحظه دچار خرابی شوند، بسیار مهم است که خرابیها به سرعت شناسایی شوند و در صورت امکان، سرویس restore شود. پس وجود monitoringهای real‑time در میکروسرویسها از اهمیت بالایی برخوردار است.
طراحی تکاملی: فرآیند تبدیل شدن معماری یک سیستم به میکروسرویسها، عموما به صورت تدریجی و در بازه زمانی طولانی انجام میشود. یعنی سیستمها در ابتدا monolith بودهاند اما در طی فرآیندی با جداسازی مؤلفهها به عنوان سرویسهای مستقل، به میکروسرویس تبدیل شدهاند. پس باید سناریوهایی برای جداسازی گام به گام کارکردهای سیستم وجود داشته باشد تا هم معماری به سمت میکروسرویس شدن پیش برود، هم سیستمی که در حال حاضر کاربران از آن استفاده میکنند، دچار مشکل نشود.
معماری میکروسرویس در چهار نسل پیشرفت کرده است.
در نسل اول، هر سرویس توسط تکنولوژیهای سبکوزن container مثل LXC بستهبندی میشود. سپس این containerها مستقر شده و مدیریت آنها در زمان اجرا توسط ابزارهای orchestration مثل mesosانجام میشود. هر سرویس باید خودش موقعیت سایر سرویسها را با استفاده از پروتکلهای ارتباطی مخصوص به آن، در نظر بگیرد. همچنین هر مکانیزم پاسخگویی به کنترل خرابی یا پایین آمدن سرویس، باید در سورس کد سرویس پیادهسازی شده باشد. هر چه تعداد سرویسهای مورد نیاز یک اپلیکیشن بیشتر شود و نیاز به استقرار مجدد سرویسها در محیطهای عملیاتی مکررا ایجاد شود، مکانیابی نمونههای سرویسها و کشف و ترمیم خطا، به یک مسئله پیچیده تبدیل میشود. همچنین اگر یک سرویس جدید با زبان برنامهنویسی متفاوتی پیاده سازی شود، استفاده مجدد از کد مدیریت خطا و مکانیابی که برای سرویسهای دیگر نوشته شده است، برای آن امکانپذیر نخواهد بود و باید این امکانات به طور مجزا برای سرویس جدید پیادهسازی شود.
برای حل بعضی از مشکلات قبلی، در نسل دوم، سرویسهای مستقل مکانیابی و کتابخانههای ارتباطی برای مدیریت خطاها معرفی شده است. سرویسها از یک سرویس مکانیابی مشترک مانند consul برای ثبت اطلاعات دسترسی خود استفاده میکنند. سرویسهای کلاینت، میتوانند به صورت داینامیک به این اطلاعات دسترسی داشته باشند، بدون این که نیاز باشد به طور صریح به مکان آن سرویس اشاره کنند. تمام نیازهای مرتبط با مدیریت خرابی نیز توسط کتابخانه مستقلی مانند Finagle انجام میشود. این استراتژی نه تنها پیاده سازی و تست نرمافزار را ساده میکند، بلکه امکان استفاده مجدد از کدهای پیچیده ارتباطی بین سرویسها را فراهم میکند؛ یعنی اگر سرویس جدیدی نیز اضافه شد، برای مکانیابی و مدیریت خطا میتواند از سرویسهای آماده استفاده کند. اما به هر حال، این کتابخانهها به مرور پیچیدهتر میشوند، همچنین چون پیادهسازی مجدد هر کدام با یک زبان برنامه نویسی جدید کار ساده و بدیهی نیست، توسعهدهندگان اغلب ناچار هستند سرویسهای جدید را با زبانی که قبلا این کتابخانهها توسعه داده شدهاند، پیادهسازی کنند؛ پس دیگر از مزایای میکروسرویسها به طور کامل بهره برده نمیشود، چرا که یکی از تعاریف اصلی در معماری میکروسرویس، امکان توسعه مستقل هر سرویس است که نباید توسعهدهنده را محدود به استفاده از تکنولوژی خاصی بکند.
در پاسخ به مشکلات نسل قبلی، در نسل سوم، پروکسیها و ماشینهای جانبی استاندارد مثلenvoy به عنوان یک واسط transparent برای سرویسها معرفی شدند. این ایده برای بالابردن امکان استفاده مجدد از سرویسها به وجود آمده است. یعنی هر سرویس بدنه مختص به خود را دارد که مربوط به نیازمندی کسبوکار آن است، اما برای ارتباط با دیگر میکروسرویسها و همچنین مدیریت خطا که یک بحث مشترک بین تمامی میکروسرویسها است، از طریق پروکسی عمل میکند. این پروکسیها میتوانند رفتار میکروسرویس را مانیتور کرده و اقدام مناسب را انجام دهند. این مورد، دقیقا ایده اصلی پشت تکنولوژی های service mesh مثل linkerd است. امکان مانتیور کردن تمام این پروکسیها نیز به وسیله یک ناحیه کنترل مرکزی وجود دارد و اپراتورها میتوانند به صورت داینامیک، رفتار چندین ماشین جانبی را، مانیتور و مدیریت کنند. با این روش میتوان کنترل ریزدانهتری روی طیف گستردهای از میکروسرویسها مثل ویژگی های ارتباطی سرویس به سرویس، مکان یابی، لود بار، تحمل خطا، مسیریابی پیامها و امنیت داشت، بدون آن که کسبوکار هر میکروسرویس درگیر این موارد باشد.
در نسل چهارم تکنولوژیهای serverless مثل Aws lambda معرفی شدند که به این الگو Faas (Function as a service) گفته میشود. با این تکنولوژی، میکروسرویسها به مجموعه ای از توابع موقت تبدیل میشوند که هر کدام از آنها میتوانند بنابر نیاز، ایجاد، بروزرسانی، جایگزین و یا حذف شوند و حتی دیگر پیچیدگیهای بالا آوردن container ها نیز وجود ندارد. در این نسل مکانیزمهای ارتباطی مانند نسل سوم بوده است، اما زیرساخت سبکتر شده است.
اپلیکیشنهای اولیه میکروسرویسها تحت تاثیر نسل جدیدی از ابزارهای توسعه، استقرار، و مدیریت نرمافزار قرار گرفتند. با افزایش محبوبیت میکروسرویسها، این ابزارها برای پشتیبانی از پایگاه کاربری گستردهتر و متنوعتر، به تکامل خود ادامه دادند و این امر منجر به ایجاد فناوریهای پیشرفتهتر شد.
در ادامه تعدادی از مهمترین فناوریها و ابزارهایی که بر توسعه، استقرار و عملکرد میکروسرویسها تاثیر گذاشتهاند، بررسی میشود. (5 فناوری اول پیش از آن که میکروسرویسها به طور عمومی پذیرفته شوند، وجود داشتند. 5 فناوری بعدی در پاسخ به محبوبیت روزافزون میکروسرویسها پدید آمدند.)
باتوجه به محبوبیت روزافزون میکروسرویسها، این احتمال وجود دارد که این معماری در موقعیتهایی استفاده شود که هزینهها بسیار بیشتر از مزایا باشد. میکروسرویسها در همهی موارد راهحل درستی نیست. موقعیتهایی هم وجود دارند که میکروسرویسها برای آنها مناسب است اما تیمها آن را با موفقیت پیادهسازی نمیکنند.
در ادامه برخی از چالش های موجود در میکروسرویسها را بررسی میکنیم.
ماژولارسازی و ریفکتور کردن سرویس: در هر رویکرد ماژولارسازی، یافتن ماژولهای مناسب با اندازه مناسب، تخصیص صحیح مسئولیتها و طراحی خوب رابطها، یک چالش است. چنین چیزی به ویژه برای میکروسرویسها و سایر رویکردهایی که در آنها طراحی بد مرزها میتواند منجر به افزایش ارتباطات شبکهای شود، صادق است. چنین افزایشی ممکن است منجر به این شود که سیستم، به دلیل عملکرد بد و بیثباتی، برای وظایف مورد نظرش، نامناسب شود. این چالش از دو جنبه قابل بررسی است: 1) ریفکتور کردن سیستم متشکل از میکروسرویسها را میتوان از طریق ابزار آسانتر کرد. اگرچه این امر ممکن است عواقب ناخواستهای در رابطه با وابستگی توسعه یا زیرساخت داشته باشد. 2) حرکت به سمت ارتباطات غیرهمزمان، استفاده فراگیرتر از کتابخانههایی که الگوهای ثبات را پیادهسازی میکنند، و محیطهای اجرایی پیچیدهتر، میتواند به رفع مشکلات ثبات کمک کند.
دانهبندی سرویس: چالش دیگر، عدم توافق در مورد اندازه مناسب میکروسرویسهاست. با این که به نظر میرسد که میکروسرویسها با توجه به نامشان باید تا حد امکان کوچک باشند، تیمهای پروژه این اصل را به روشهای بسیار متفاوتی تفسیر میکنند. بعضی از تیمها دارای میکروسرویسهایی با تنها چند یا چند ده خط کد هستند. بعضی دیگر چند هزار خط کد را به همراه چند ده کلاس و موجودیتهای پایگاه داده، در یک میکروسرویس کپسوله میکنند. ارتباط بین این سرویسهای با اندازه متوسط میتواند همزمان یا غیرهمزمان باشد. همهی این رویکردها قابل قبول هستند. با این حال با توجه به این که همه از نام میکروسرویسها استفاده کرده اند، این امکان وجود دارد که مجموعهای از الگوها ایجاد شود تا در هنگام تقسیم دامنه به میکروسرویسها و تعیین اندازه هر سرویس، به تصمیمگیریهای طراحی کمک کند.
ادغام Front-End : رابط کاربری (UI) جزء مهمی در معماری میکروسرویسهاست. با این حال، رابط کاربری مرکز توجه بسیاری از طرفداران میکروسرویس، که معمولا معمارانی هستند که با جنبههای back end سروکار دارند، نبوده است. این امر میتواند منجر به سیستمهایی شود که در آن یک front‑tend یکپارچه (monolithic) از تعدادی میکروسرویس back‑end استفاده میکند. چنین معماریهایی ممکن است گاهی اوقات خوب باشند، اما اغلب مانع اهداف میکروسرویسها میشوند، چون همهی جنبههای منفی معماری monolithic هنوز وجود دارد. ماژولار کردن front‑endها در انواع مختلف (web، native، hybrid)، به همراه ارتباط آنها به عنوان بخشی از میکروسرویسها یا یک موجودیت همکار، برای کمک به سازمانها برای پیادهسازی یک محیط میکروسرویس full stack، به شدت مورد نیاز است.
نظارت و مدیریت منابع: با رشد اندازه و پیچیدگی اپلیکیشنهای میکروسرویس، تعداد و تنوع منابع زیرساختی (مانند ماشینهای مجازی، containerها، سرویسها، پیامها و logها)، که باید به طور مداوم در زمان اجرا نظارت و مدیریت شوند، نیز افزایش مییابد. به علاوه، ممکن است سرویسها در چندین ناحیه و منطقه دسترسی مستقر شوند، چنین چیزی چالش جمعآوری اطلاعات درباره وضعیت و رفتار آنها را تشدید میکند. در نهایت، با افزایش سطح خودکارسازی که فناوریهای نظارت فعلی ارائه میدهند، ممکن است توسعهدهندگان خود را در میان سیلی از رویدادهای نظارتی ببینند و نتوانند تصمیمات مدیریتی به موقع بگیرند. یکی از مسائل مهم در اینجا این است که چگونه آستانههای هشدار و فیلترهای مناسبی تعریف شود، تا هر زمان که مشکلی پیش آمد به توسعه دهندگان اطلاع داده شود، بدون این که اطلاعات اضافی یا نامربوط بارگذاری شود. موضوع چالش برانگیزتر این است که چگونه از رویدادها و اقدامات گذشته یاد بگیریم، تا تصمیمات مدیریت منابع را بهتر اطلاع دهیم یا آنها را خودکار کنیم. مانند بسیاری دیگر از سناریوهای کلان دادهها (big data)، تئوری کنترل و یادگیری ماشین باید نقش مهمی را در افزایش مقیاسپذیری نظارت و مدیریت منابع میکروسرویسها ایفا کنند.
خرابی (Failure)، بازیابی (Recovery) و خود ترمیمی (Self-Repair): میکروسرویسها، مانند هر نوع سیستم توزیع شده، معمولا شکننده هستند. ممکن است به دلایل زیادی، مانند مشکلات شبکه، سختافزار، یا اپلیکیشن، غیرقابل دسترس شوند یا به سادگی شکست بخورند. به دلیل وابستگیهای سرویس، هر سرویس میتواند به طور موقت از دسترس خارج شود. در هر تنظیم توزیعشدهای، ارتباطات هر از گاهی با شکست مواجه میشود. با توجه به یک سیستم میکروسرویس به عنوان یک کل، خرابیهای ارتباطی احتمالا اغلب به دلیل تعداد پیامهای ارسالی بین سرویسها، رخ میدهد. توسعه دهندگان باید به منظور به حداقل رساندن تأثیر قطعیهای جزئی، سرویسهایی مقاوم در برابر خطا (fault tolerant) بسازند که بتوانند به خوبی به انواع خرابیها پاسخ دهند. محققان و متخصصان راههایی برای جداسازی خرابیها پیشنهاد کردهاند تا در سراسر سیستم توزیع شده منتشر نشوند. با این حال، کار بیشتری برای مدیریت خودکار خرابی و راهحلهای خود ترمیم برای ترمیم سیستم پس از خرابی، مورد نیاز است.
در این پژوهش به منظور شناسایی ضدالگوها در میکروسرویسها مقالهای را مورد بررسی قرار دادیم که در آن با 72 توسعهدهنده که تجربه توسعه سیستمهای مبتنی بر میکروسرویسها را داشتند، مصاحبه شده بود. از نتیجه این مصاحبه، Bad Smellهایی در زمینه میکروسرویسها شناسایی شد. Bad Smellها همان ضدالگو ها هستند. در واقع Bad Smellها یا Bad Architectural Smellها علائمی از طراحی ضعیف هستند که میتوانند منجر به کاهش قابلیت فهم و نگهداری کد شوند.
Bad Smellهای شناسایی شده در این مقاله به ترتیب اولویت در ادامه آمده است.
Wrong Cuts: عدم تفکیک میکروسرویسها بر اساس کارکردهای کسبوکار و توجه به لایهبندیهای فنی
Hard-coded endpoints: تعریف ثابت IPو PORT سرویسها در مبدا و مقصد یک ارتباط که باعث میشود پویایی سیستم کاهش پیدا کند و امکان جایگزین کردن سرویسها نیز نباشد.
Cyclic dependency: وجود حلقه زنجیره فراخوانی بین میکروسرویسها میتواند باعث انتشار خطا و خرابی در مجموعه سیستم شود.
Shared persistency: وجود دیتابیسهای مشترک با جداول و موجودیتهای مشترک بین میکروسرویسها و امکان دسترسی همزمان به این دادهها میتواند سازگاری دادهها را نقض کند.
API versioning: اگر چندین API ورودی برای یک میکروسرویس وجود داشته باشد و در نامگذاری آنها تفاوتی نباشد، میتواند در استفاده درست از آن میکروسرویس مشکل ایجاد کند.
ESB usage: اگر واسط ارتباطی بین میکروسرویسها یک گذرگاه سنگینوزن مثل ESBباشد، بار زیادی به مجموعه سیستم تحمیل میشود.
Not having an API gateway: اگر درگاه ارتباطی بین میکروسرویسها وجود نداشته باشد و هر کدام جداگانه به یکدیگر متصل شوند، شبکهی ارتباطی بین آنها بسیار پیچیده شده و مدیریت خطا سخت میشود.
Inappropriate service intimacy: دسترسی هر میکروسرویس به دادههای خصوصی میکروسروس دیگر باعث نقض استقلال هر یک میشود.
Shared libraries: وجود کتابخانههای مشترک در پیادهسازی میکروسرویسهای متفاوت باعث میشود که تغییر در هر یک از آنها روی تعداد زیادی میکروسرویس اثر بگذارد.
Too many standards: زیادهروی در استفاده از تکنولوژیها و زبانهای برنامه نویسی متفاوت در پیادهسازی میکروسرویسها باعث ایجاد پیچیدگیهای زیاد در سیستم خواهد شد.
Microservice greedy: عدم تفکیک درست سیستم به میکروسرویسها و زیادهروی در استفاده از آنها باعث پیچیدگی خواهد شد؛ برای مثال برای نمایش یک یا دو صفحه استاتیک HTML یک میکروسرویس در نظر گرفته شود که ضرورتی ندارد.
نتفلیکس یکی از اولین شرکتهایی بود که معماری میکروسرویسها را پذیرفت و از معماری monolithic به میکروسرویسها در بستر cloud مهاجرت کرد. در حقیقت، این شرکت مفاهیم میکروسرویسها را، ، قبل از این که حتی به طور آکادمیک معرفی شده باشند، اجرا کرد. بر این اساس میتوانیم نتفلیکس را به عنوان یک راهبر تکنولوژی و پیشگام در زمینه مهاجرت به میکروسرویسها مطرح کنیم. پس از نتفلیکس شرکتهایی مانند گوگل، توئیتر، IBMو آمازون به میکروسرویسها مهاجرت کردند.
در حدود 20 سال قبل، نتفلیکس مانند کلوپی بود که با دادن حق اشتراک، فیلمها را در قالب DVD عرضه میکرد؛ اما در حال حاضر یکی از بزرگترین سیستمهای پخش زنده در بستر اینترنت است. همان طور که مشخص است، برای چنین نیازمندی متفاوت و وسیعی که در طول زمان برای این شرکت ایجاد شد، معماری گذشته دیگر پاسخگو نبود و لازم بود تصمیمات متفاوتی در خصوص ساختار معماری آن گرفته شود.
مهاجرت نتفلیکس به میکروسرویسها بیشتر از دو سال به طول انجامید. چالشهای زیادی در این بازه رخ داد، از جمله این که نتفلیکس می بایست هم زیرساخت های ابری خود را نگهداری میکرد تا از صحت عملکرد آن مطمئن شود، هم ساختار سنتی خود را حفظ میکرد، تا کاربران به مشکل نخورند؛ این مسئله هزینه زیادی را به این شرکت وارد کرد. به علاوه انتقال حجم زیادی از داده به سرورهای جدید، مشکلاتی از قبیل لود زیاد بار و کارآیی پایین را در بر داشت.
در این پژوهش معماری نتفلیکس را مورد مطالعه قرار دادیم و فتاوریها و ابزارهای به کارگرفته شده در آن را شناسایی کردیم. در ادامه به شرح جزئیات این معماری و فناوریها و ابزارهایهای آن میپردازیم.
نتفلیکس بلوکهای بسیار جالبی را برای مدیریت و پیادهسازی میکروسرویسها ساخته و به صورت open‑source ارائه کرده است. سایر شرکتها میتوانند از این فناوریها و ابزارها برای پیادهسازی میکروسرویسها استفاده کنند.
Netflix OSS (Netflix Open Source Software Center) مجموعهای از فریمورکها و کتابخانههایی است که نتفلیکس برای حل برخی مشکلات سیستمهای توزیعشده (در زمینه مقیاسپذیری) نوشته است.
الگوهای کشف سرویس (service discovery)، متعادلسازی بار (load balancing)، تحمل خطا (fault-tolerance) مفاهیم بسیار مهمی برای سیستمهای توزیعشده مقیاسپذیر هستند و نتفلیکس راهحلهای خوبی برای آنها ارائه میکند.
همانطور که پیشتر گفتیم، نتفلیکس از سبک معماری میکروسرویسها استفاده میکند. در این معماری، سرویسها باید مستقل از یکدیگر باشند، به عنوان مثال، سرویس ذخیرهسازی ویدیو از سرویسی که وظیفه رمزگذاری ویدیوها را بر عهده دارد جدا میشود.
نتفلیکس برای افزایش قابلیت اطمینان (reliability) میکروسرویسها:
نتفلیکس تعدادی از سرویسها را به عنوان سرویسهای حیاتی جدا میکند و وابستگی آنها به سایر سرویسها را به حداقل میرساند. میکروسرویسهای حیاتی شامل قابلیتهای اساسی مانند جستجوی ویدئو، پیمایش فیلمها و پخش ویدئو هستند. چنین روشی منجر به دسترسیپذیری (availability) بالا میشود و در بدترین سناریو حداقل یک کاربر قادر به انجام کارهای اساسی میباشد.
طراحی سرویسها به گونهای است که اگر یکی از endpointها دچار خطا شد یا درخواست به موقع سرویسدهی نشد، بتوان به سرور دیگری سوئیچ کرد و کار را انجام داد. در واقع به جای تکیه بر یک سرور خاص و حفظ وضعیت (state) در آن سرور، میتوان درخواست را به سرور (service instance) دیگری هدایت کرد. اگر سروری از کار بیفتد با سرور دیگری جایگزین خواهد شد.
نتفلیکس روی دو cloud کار میکند: AWS و Netflix Open Connect (Netflix CDN)
اپلیکیشن نتفلیکس دارای 3 مؤلفه اصلی است:
Client: مؤلفه client دستگاه یا رابط کاربری است که از آن برای مرور و پخش ویدئوها استفاده میشود، مانند لپتاپ، تلوزیون، تلفن همراه و XBOX.
OC (Open Connect) یا Netflix CDN: CDN (Content Delivery Network) شبکهای از سرورهای توریع شده در مکانهای جغرافیای مختلف است. Open Connect، CDN سفارشی خود نتفلیکس است. همه موارد مربوط به پخش ویدئو در این مؤلفه کنترل میشود. در اپلیکیشن نتفلیکس ویدئو از نزدیک ترین سرور (OC)، به جای سرور اصلی، پخش میشود و در نتیجه پاسخدهی سریعتر است.
Backend: مؤلفه Backend شامل پایگاهدادهها، سرورها، monitoring، موتور recommendation، سرویسهای background و ... میباشد. تمامی موارد (به غیر از موارد مربوط به پخش ویدئو) مانند محتوای جدید، پردازش ویدئوها، توزیع ویدئوها روی سرورهای مختلف در نفاط مختلف جهان و مدیریت ترافیک شبکه در این مؤلفه کنترل میشود. AWS EC2، AWS S3، Cassandra، Hadoop و Kafkaنمونههایی از سرویسهای Backend هستند. اکثر فرآیندها و درخواستها مانند login، recomendationها، تاریخچهی کاربران، صورتحسابها، و پشتیبانی مشتری، توسط سرویسهای وب آمازون (AWS) انجام میشود.
Frontend نتفلیکس با استفاده از ReactJS نوشته شده است.
نتفلیکس از دو پایگاه داده مختلف یعنی MySQL و Cassandra استفاده میکند.
نتفلیکس داده هایی مانند اطلاعات صورتحساب، اطلاعات کاربر و اطلاعات تراکنش را در MySQL ذخیره می کند (زیرا این دادهها به قابلیتهای ACID نیاز دارند).
پایگاه داده MySQL نتفلیکیس روی instance های Amazon EC2 با موتور InnoDB مستقر شده است.
Apache Cassandra یک پایگاه داده توزیع شده NoSQL متنباز (open-source) است که برای مدیریت حجم زیادی از دادهها طراحی شده است.
نتفلیکس از Cassandra برای مقیاس پذیری (scalability)، عدم وجود نقاط شکست واحد (single points of failure) و استقرارهای بین منطقهای (cross-regional deployment) استفاده می کند.
نتفلیکس انواع داده ها را در نمونه های Cassandra DB خود ذخیره می کند. به عنوان مثال، تاریخچه مشاهده هر کاربر در Cassandra ذخیره می شود. در ابتدا تاریخچه مشاهده در یک سطر (row) ذخیره میشد، اما با افزایش کاربران نتفلیکیس، حجم دادهها نیز افزایش یافت و منجر به هزینه عملیاتی بیشتر و کارایی کمتر اپلیکیشن شد. از این رو نتفلیکس معماری ذخیره سازی داده را با دو هدف بازطراحی کرد: 1) Storage Footprint کوچکتر، 2) ثبات کارایی خواندن/نوشتن با افزایش بازدید به ازای هر کاربر. برای حل این مسئله سطرهای قدیمی فشرده شدند و دادهها را به دو نوع تقسیم شدند:
تاریخچه مشاهده زنده Live Viewing History (LiveVH): تعداد کمی رکوردهای مشاهده اخیر با به روز رسانیهای مکرر به صورت غیر فشرده ذخیره می شوند.
تاریخچه مشاهده فشرده شده Compressed Viewing History (CompressedVH): تعداد زیادی رکوردهای مشاهده قدیمی با بهروزرسانیهای نادر، به منظور کاهش فضای ذخیره سازی، به صورت فشرده ذخیره می شوند. این نوع داده به ازای هر RowKey در یک ستون واحد ذخیره می شود.
در این بخش به بررسی تعدادی از ابزارهایی که Netflix در معماری خود استفاده کرده است، میپردازیم.
نتفلیکس از سرویس ELB آمازون برای هدایت ترافیک به سرویسها استفاده میکند. در ELB ابتدا بار (load) روی مناطق (zoneها) و سپس بین سرورها (instanceها) موازنه و بالانس میشود این سرویس از تکنیک Round Robin Balancing برای تعادل بار استفاده میکند. در نهایت ELBدرخواست را به درگاه API میفرستد.
نتفلیکس از ZUUL به عنوان API gatewayاستفاده میکند. ZUUL یک gateway service است که dynamic routing، monitoring، انعطافپذیری و امنیت را فراهم میکند.
در ادامه نحوه عملکرد ZUUL در نتفلیکس را بررسی میکنیم.
سرور ZUUL به صورت dynamic درخواستها را به اپلیکیشنهای backend هدایت میکند. Netty handlerها در جلو و پشت فیلترها مسئولیت رسیدگی به پروتکل شبکه، وبسرور، مدیریت اتصال و proxying را برعهده دارند. وقتی درخواستی به Netty server رسید، Netty server آن درخواست را به inbound filter میفرستد. Inbound filterها قبل از پروکسی درخواست اجرا می شوند و برای احراز هویت، مسیریابی یا تزئین درخواست استفاده میشوند. سپس Endpoint filter برای بازگرداندن یک پاسخ استاتیک (static response) یا پروکسی درخواست به سرویس backendاستفاده میشود. پس از بازگرداندن پاسخ outbound filterها اجرا می شوند و برای مواردی مانند معیارها یا افزودن/حذف هدرهای سفارشی استفاده میشوند.
در یک سیستم پیچیده توزیع شده، ممکن است یک سرور به پاسخ سرور دیگری متکی باشد. وابستگیهای بین سرورها میتواند منجر به تأخیر شود و اگر یکی از سرورها در نقطهای از کار بیفتد، ممکن است کل سیستم از کار بیفتد. کتابخانه Hystrix با افزودن منطق تحمل تاخیر (latency tolerance) و تحمل خطا (fault tolerance)، تعاملات بین سرویسهای توزیع شده را کنترل میکند. Hystrix این کار را با ایزوله کردن نقاط دسترسی بین سرویسها، سیستمهای remote و کتابخانههای شخص ثالث انجام میدهد.
مثال: اگر میکروسرویسی که لیست فیلمهای مناسب را به کاربر ارائه میکند، fail شود، ترافیک به میکروسرویسی که 10 فیلم برتر را برمیگرداند تغییر مسیر میدهد و در نتیجه خرابی کنترل میشود.
مزایای Hystrix:
به این نکته توجه داشته باشید که Netflix Hystrix دیگر در حال توسعه فعال نیست و در حال حاضر در حالت تعمیر و نگهداری است. برخی از پروژه های داخلی در حال حاضر با resilience4j در حال ساخت هستند.
Titus یک پلتفرم مدیریت container است که اجرای container مقیاس پذیر و قابل اعتماد و ادغام cloud-native با Amazon AWS را فراهم میکند. Titus توسط نتفلیکس ساخته شده است و برای تقویت استریم، recomendation و سیستمهای محتوا استفاده میشود.
Titus هزاران نمونه AWS EC2 را مدیریت میکند و روزانه صدها هزار container را برای workloadهای دستهای و سرویس راهاندازی میکند.
Titus میتواند تصاویر بستهبندیشده (images packaged) را بهعنوان containerهای Docker اجرا کند و در عین حال امنیت و قابلیت اطمینان (reliability) بیشتری را در مورد اجرای containerفراهم کند.
میتوان Titus را به عنوان نسخه نتفلیکس Kubernetes در نظر گرفت.
Chaos Monkey اسکریپتی است که به طور مداوم در تمام محیطهای Netflix اجرا میشود و با خاموش کردن تصادفی نمونههای سرور باعث بی نظمی و هرج و مرج (chaos) میشود. از این رو، در حین نوشتن کد، توسعه دهندگان نتفلیکس به طور مداوم در محیطی از سرویسهای غیرقابل اعتماد و قطعی های غیرمنتظره فعالیت میکنند. در واقع Chaos Monkey نمونهها را به طور تصادفی متوقف (terminate) میکند تا اطمینان حاصل کند که توسعهدهندگان، سرویسها را به گونهای پیادهسازی میکنند که در برابر خرابیها انعطاف پذیر باشد.
EVCache یک راه حل ذخیره سازی cache توزیع شده مبتنی بر Memcached و Spymemcached است که به خوبی با Netflix OSS و زیرساخت AWS EC2 یکپارچه شده است.
در بسیاری از اپلیکیشنها، حجمی از دادهها به طور مکرر استفاده میشوند. برای پاسخ سریعتر، میتوان این دادهها را در حافظه cacheبسیاری از endpointها ذخیره کرد و آنها را بهجای سرور اصلی از cache دریافت کرد. این کار باعث کاهش بار سرور اصلی می شود، اما مشکل اینجاست که اگر گره دچار خرابی شود، تمام حافظه پنهان از دست میرود و کارایی برنامه را تحت تأثیر قرار میدهد. برای حل این مشکل نتفلیکس لایه cache سفارشی خود به نام EVCache را ساخته است. EVCache در واقع یک پوشش (wrapper) در اطراف Memcached است.
هر cluster دارای تعداد زیادی گرهی Memcached است و آنها نیز cache clientهایی دارند. دادهها در سراسر cluster در یک منطقه (zone) به اشتراک گذاشته میشود و چندین نسخه کپی از cache در گرههای خرد شده (sharded node) ذخیره می شود. در هر write برای client، تمام گرهها در همه clusterها بهروزرسانی میشوند، اما read از حافظه cache، فقط به نزدیکترین cluster و گرههای آن (نه همه clusterها و گرهها) ارسال میشود. در صورتی که یک گره در دسترس نباشد، از گره دیگری که در دسترس است میخواند.
این رویکرد کارایی (performance)، دسترسپذیری (availability) و قابلیت اطمینان (reliability) را افزایش میدهد.
به طور سنتی ذخیرهسازی cache روی RAM انجام میشود، اما ذخیره کردن حجم زیادی از دادهها در RAM هزینه زیادی دارد. از این رو نتفلیکس تصمیم گرفت تا برخی از داده های cache را به SSDمنتقل کند.
Apache Chukwa یک سیستم جمعآوری داده open-source برای نظارت بر سیستمهای توزیع شده بزرگ است. Apache Chukwa بر روی سیستم فایل توزیع شده Hadoop (HDFS) و چارچوب Map/Reduce ساخته شده است و مقیاس پذیری و استحکام Hadoop را به ارث برده است. Apache Chukwa همچنین دارای مجموعهای منعطف و قدرتمند از ابزارها برای نمایش (displaying)، نظارت (monitoring) و تجزیهوتحلیل (analyzing) دادههای جمعآوریشده است.
نتفلیکس تقریبا 500 میلیارد رویداد داده با مصرف 1.3 پتابایت در روز و 8 میلیون رویداد با مصرف 24 گیگابایت در ثانیه در زمان اوج مصرف، پردازش میکند. این رویدادها شامل اطلاعاتی مانند لاگهای خطا، فعالیتهای رابط کاربری، رویدادهای اجرایی، فعالیتهای مشاهده ویدئو، و رویدادهای اشکالزدایی میباشد.Apache Chukwa این رویدادها را از بخشهای مختلف سیستم جمعآوری میکند و آنها را در فرمت Hadoop file sequence (S3) می نویسد.
Apache Kafka یک پلتفرم استریم رویداد توزیع شده open-source است که توسط هزاران شرکت برای piplineهای داده با کارایی بالا، تجزیهوتحلیل جریان دادهها، یکپارچه سازی دادهها و اپلیکیشنهای حیاتی استفاده میشود.
در نتفلیکس Apache Kafka مسئول انتقال دادهها از kafkaجلویی به موارد مختلفی از جمله S3، Elastic Search، و kafka ثانویه است. مسیریابی این پیامها با استفاده از فریمورک Apache Samzaانجام میشود.
ترافیک ارسالی توسط Chukwa میتواند استریمهای کامل یا فیلتر شده باشد، بنابراین گاهی اوقات ممکن است لازم باشد فیلترهای بیشتری روی استریمهای Kafka اعمال شود. به همین دلیل router از یک عنوان Kafka به عنوان دیگر Kafka در نظر گرفته میشود.
Apache Samza یک فریمورک open-source محاسباتی غیرهمزمان برای پردازش استریم است. به عبارت دیگر Apache Samza یک موتور پردازش داده مقیاس پذیر است که پردازش و آنالیز دادهها را به صورت real-time ممکن میسازد.
Apache Samza در ارتباط با Apache Kafka توسعه یافته است.
Elasticsearch یک موتور جستجو و آنالیز توزیع شده است که بر روی Apache Lucene ساخته شده است. در واقع Elasticsearc یک موتور جستجوی full-text توزیع شده با قابلیت multitenant است.
نتفلیکس از Elasticsearch برای مصورسازی داده ها، پشتیبانی مشتری و برای تشخیص خطا در سیستم استفاده می کند. با Elasticsearch به راحتی می توان وضعیت سیستم را monitorو لاگهای خطا و خرابی ها را عیب یابی کرد. به عنوان مثال، اگر کاربر قادر به پخش ویدئو نباشد، مسئول خدمات مشتری این مشکل را با استفاده از elastic search حل می کند. تیم پخش ویدئو، کاربر را جستجو میکند تا بداند چرا ویدیو در دستگاه کاربر پخش نمیشود. آنها از تمام اطلاعات و رویدادهایی که برای آن کاربر خاص اتفاق میافتد آگاه میشوند و متوجه میشوند که چه چیزی باعث خطا در استریم ویدیو شده است. همچنین برای پیگیری استفاده از منابع و شناسایی مشکلات ثبت نام (signup) یا ورود (login) از elastic search استفاده میشود.
Apache Spark یک موتور چند زبانه برای اجرای مهندسی داده، علم داده و یادگیری ماشین در ماشینها یا خوشههای single‑node است. نتفلیکس از Apache Spark و یادگیری ماشین برای پیشنهاد فیلم (Movie recommendation) استفاده میکند.
تا سال 2000، با وجود اینکه معماری آمازون از لایههای مختلفی تشکیل شده بود، اما همچنان یک معماری یکپارچه به حساب میآمد، چرا که بخشهای مختلف، وابستگی زیادی به هم داشتند. از آن سال با توجه به پیچیدهتر شدن کسبوکار آمازون و وسعت زیاد سورس کد آن، این شرکت به سمت تغییر معماری به میکروسرویسها حرکت کرد. آمازون جز اولین شرکتهایی نبود که قصد مهاجرت از معماری monolithic به میکروسرویس را داشت، اما به دلیل فراهم آوردن ساختارهای Cloud مانند Amazon Web Service میتوان گفت هموارکنندهی راه سایر شرکتها برای انجام این مهاجرت بوده است؛ چرا که سرویسهایی را در فضای Cloud ایجاد کرد که پیچیدگی توسعه میکروسرویسها را بسیار کاهش میداد. در معماری میکروسرویس وبسایت آمازون نیز از سرویسهایAWS استفاده شده است.
حال که دانستیم از سرویسهای (AWS) در توسعه معماری میکروسرویس در آمازون استفاده شده است، ابتدا از دیدگاه کسبوکار به بررسی میکروسرویسهای آن میپردازیم.
وبسایت آمازون از سرویسهایی مانند Search، Wish List، Order Tracking، Pricing، Logistics، Items، Purchase Orders، Notification، Recommendation، Historical Order، Order Processing، Archival، Warehouse و Inventory Management تشکیل شده است.
هر یک از سرویسهایی که در بالا گفته شد، یک فعالیت مستقل دارند و ارتباطات آنها با یکدیگر، سیستم اصلی وبسایت فروشگاهی آمازون را تشکیل میدهد. نکتهای که در مورد آمازون، حائز اهمیت است این است که چون آمازون یک فروشگاه اینترنتی است دائما باید با تامینکنندگان کالاها در ارتباط باشد و حجم زیادی از دادهها از بیرون سیستم وارد آن میشوند و پس از انجام پردازشهایی، این دادهها درون دیتابیسهای داخلی سیستم ذخیره میشوند و در اختیار کاربر قرار میگیرد. پس یکی از سناریوهای اصلی در معماری میکروسرویس آمازون، کنترل جریان دادههایی است که میتواند از بیرون از سیستم وارد شده و باید پردازش شوند. دو جریان کلی در معماری آمازون وجود دارد: 1) جریان جستجوی محصولات توسط کاربر و 2) جریان خرید کاربر. نقش اصلی در هدایت دادهها در این دو جریان بر عهده مولفه Brokerاست که ترتیب انجام عملیات و وابستگی بین میکروسرویسها را تعیین میکند. در ادامه مثالی از عملکرد آن آمده است.
اطلاعاتی که از سمت تامینکنندگان میآید، توسط Inbound Serviceها دریافت شده که به آنها سرویسهای ورودی گفته میشود. وظیفهی این سرویسها واکشی دادهها از سرویسهای خارجی و انتقال آنها به مولفههای سیستم آمازون است. برای مثال زمانی که تامینکنندهای محصول جدیدی را به خدماتش اضافه میکند و یا با تامین کنندهی جدیدی قرارداد بسته میشود، این اطلاعات توسط Inbound Serviceها دریافت شده و وارد Brokerمیشود. میکروسرویسهای دیگر در سیستم مثل Item Serviceهمواره از Inbound Service، Listen میکند و زمانی که متوجه ورود و یا تغییر آیتمی شد، API های Add یا Updateرا در دیتابیس مخصوص خود، صدا میزند تا دادههایش بروز باشند. همانطور که قبلا نیز اشاره شد، معمولا هر میکروسرویس، دیتابیس مختص خود را دارد. در این مثال نیز سرویس Item از یک دیتابیس NoSQL استفاده میکند؛ زیرا قبل از انجام هر پردازشی روی دادهها میخواهد فقط وضعیت آیتمهای موجود در سیستم را در لحظه و آپدیت شده داشته باشد؛ حال این اطلاعات میتواند از سمت یک تامینکنندهی کالای دیجیتال آمده باشد یا یک تامینکنندهی مد و پوشاک.
پس از این که ورود این اطلاعات در دیتابیس میکروسرویس Itemثبت شد، پیام به Broker ارسال میشود و میکروسرویس Search Consumer با Listen کردن از آن، دادهها را دستهبندی و فیلتر کرده و در دیتابیس خود که Elastic Search Clusterاست، ذخیره میکند. دلیل استفاده از این دیتابیس، کارآیی بالای آن در انجام جستجوهای متنی و Fuzzy Search است. حال که دادهها پردازش شده و در یک دیتابیس کارا ذخیره شدند، میکروسرویس Search میتواند از این دیتابیس استفاده کند و برای مثال کاربری که در Home Pageآمازون، محصولی را جستجو کند، اطلاعات از این دیتابیس خوانده میشود. علاوه بر Inbound Serviceها که میتوانند اطلاعاتی را به Broker منتقل کنند، مولفهی Apache Spark Clusterنیز گزارشهایی از وضعیت جستجوها در سیستم، میسازد که میتواند این اطلاعات را به عنوان Recommendation در اختیار Brokerقرار دهد و پس از آن در میکروسرویس جستجو از آن استفاده شود.
در جریان خرید کاربر، سازگاری دادهها بسیار اهمیت دارد، چرا که اگر محصولی، میتواند به عنوان خرید قطعی وارد سبد خرید کاربر شود، باید اطلاعات آن با موجودی آن کالا در سیستم همخوانی داشته باشد. پس در میکروسرویس Ordersاز دیتابیسهای رابطهای مثل My SQLاستفاده میشود که بتوان خاصیتهای ACID را در آن به طور موثری رعایت کرد. اما نکتهای که وجود دارد این است که برای وبسایت آمازون، با حجم ترافیک بسیار زیادی که دارد، شاید استفاده از دیتابیسهای رابطهای به یک گلوگاه تبدیل شود و بر روی کارآیی سیستم اثر بگذارد. یک راه حل برای این مسئله این است که سفارشاتی که به وضعیت نهایی خود رسیدهاند، یعنی تحویل داده شده یا لغو شدهاند، توسط میکروسرویس دیگری به نام Order Processing، دنبال شده و به دیتابیسهای NoSQLدیگری وارد شوند. این موضوع باعث میشود در مراجعات بعدی کاربر و برای انجام پردازشهای Recommendation دیگر نیاز به استفاده از دیتابیسهای رابطهای نباشد و کارآیی سیستم بالا میرود.
در این سناریو نقش Broker به عنوان واسط در توالی بین میکروسرویسها و نحوهی ذخیرهسازی دادهها بررسی شد.
وبسایت آمازون روی AWS کار میکند و بخشی از ابزارهایی که از آن برای پیادهسازی میکروسرویسها استفاده میشود، به تفکیک فناوری در ادامه آمده است.
هر میکروسرویس روی یک Container به نام Amazon Elastic Container Service(Amazon ECS) یا Elastic Kubernetes Service (EKS) مستقر شده است.
سه روش معمول برای پیادهسازی میکروسرویسها عبارتند از:
برای ذخیرهسازی امن دادهها از Amazon simple storage service (S3) و Elastic Cache استفاده میشود.
برای مدیریت ارتباطات بین میکروسرویسها از Amazon API Gateway و برای تعیین داینامیک مسیر برای هدایت ترافیک از AWS Elastic Load Balancing و برای orchestration از Amazon ECS Service Discovery و AWS App Mesh استفاده شده است.
برای مانیتورکردن ترافیک در سیستم از AWS CloudTail استفاده میشود و برای مانیتورکردن زیرساختها (دیتابیسها، کانتینرها و غیره) از Amazon CloudWatch استفاده میشود.
برای انتقال و ایجاد صفهای پیام از Simple Queue Service (SQS) استفاده شده و برای ارسال نوتیفیکیشن از سرویس Amazon SNS استفاده میشود.
برای استفاده از پایگاهدادههای رابطهای و غیررابطهای و Cloud native از سرویسهای زیر استفاده میشود:
Amazon RDS با دیتابیسهای رابطهای مثل PostgreSQL، MariaDB، MySQL، Oracle و Microsoft SQL Server سازگار است.
Amazon Neptune graph database، DocumentDB و DynamoDB با دیتابیسهای غیررابطهای سازگار هستند.
Amazon Aurora خود یک دیتابیس cloud native است.
SoundCloud در حدود سال 2012 معماری خود را به میکروسرویسها تغییر داد. وجود مشکلاتی مانند بروز ریسکهای زیاد در هنگام اعمال تغییرات روی سیستم monolith، زمانگیر بودن تغییرات، افزایش حجم دادهها و دشوار بودن نگهداری و پشتیبانی از پایگاه دادهها، سبب شد تا تیم SoundCloudبه معماری میکروسرویسها مهاجرت کند.
در روند تکاملی معماری میکروسرویسها در SoundCloudمیتوانیم ابتدا الگوی BFF (Backends for Frontends)، سپس الگوی VAS (Value Added Service) و پس از آن رویکرد Domain Gateway را در نظر بگیریم.
در ادامه به بررسی لایههای سرویس در معماری SoundCloud، رویکردهای BFF، VASو Domain Gateway و مزایا و معایب آنها و تعدادی از ابزارهای استفاده شده در معماری SoundCloud میپردازیم.
Edge (لبه): این لایه قابلیتهای درگاه API را فراهم میکند و محل قرار گرفتن BFFها (Backends for Frontends) میباشد. BFFها، APIهای اختصاصی هستند که متناسب با نیازهای مشتری خاص طراحی شده اند.
Value Added (ارزش افزوده): سرویسهای این لایه، دادههای سایر سرویسها را میگیرند و به روشی آنها را پردازش میکنند تا تجربیات غنی برای کاربران بسازند.
Foundation (بنیاد): سرویس بنیادی، سرویس سطح پایینی است که بلوکهای سازنده حول یک دامنه را فراهم میکند.
BFFها مسئولیتهای درگاه API، از جمله احراز هویت، پاکسازی header وکنترل حافظه cache، را انجام میدهند. SoundCloud دهها BFF را اجرا میکند. ترافیک خارجی وارد شده به مراکز داده SoundCloud، توسط یکی از BFFها پردازش میشود. در مجموع BFFها به صدها میلیون درخواست در ساعت رسیدگی میکنند.
BFFها از یک کتابخانه داخلی استفاده میکنند که قابلیت های edge و همچنین نقاط توسعه برای رفتار سفارشی را فراهم میکند. نسخههای جدید کتابخانه به صورت نیمهخودکار در عرض چند ساعت در همه BFFها منتشر میشود.
نمونههایی از BFFها در SoundCloud عبارتند از:
مزایای الگوی BFF
معایب الگوی BFF
الگوی VAS معماری تمیزتر و جداسازی بهتر دغدغهها را ممکن میسازد و یک نقشه راه روشن برای معماری میکروسرویسها فراهم میکند.
Domain (دامنه): دغدغه کاربر یا کسبوکار، که در طراحی مرزها حول یکپارچهسازی سرویسها مورد استفاده قرار میگیرد.
Entity (موجودیت): موجودیت شیئی است که دارای شناسه مستقل و چرخه حیات است.
Value Objects (اشیاء ارزش): اشیاء ارزش شامل فرادادههای (metadata) مربوط به یک موجودیت معین هستند. این اشیاء با چرخه حیات موجودیت نیز در ارتباط هستند.
Aggregate (تجمع): Aggregate مجموعه ای از یک یا چند موجودیت مرتبط است و دارای یک موجودیت ریشه به نام Aggregate Root است. Aggregateها می توانند ارجاعاتی به موجودیتهای دیگر داشته باشند، و این به سرویسهای مصرفکننده بستگی دارد که سایر سرویسها را برای ترکیب ارجاعات موجودیت فراخوانی کنند.
سرویسهای ارزش افزوده، سرویسهای تجاری هستند که مسئول برگرداندن یک موجودیت (Entity) و اشیاء ارزش (Value Objects) مرتبط با آن (به عبارت دیگر، Aggregate) به فراخوانی کننده (caller) هستند. توجه به این نکته مهم است که VASمسئول ترکیب متادیتا برای هیچ یک از موجودیتهای مرتبط نیست. VAS امکان جداسازی دغدغهها را به خوبی فراهم میکند؛ یک نقطه متمرکز وجود دارد که در آن متادیتا و قوانین مجوز (authorization) یک موجودیت معین تعریف میشود. در ادامه سرویس ارزش افزوده (VAS) میتواند فراخوانی سرویسها را برای ترکیب و صدور مجوز Aggregateها و سپس برگرداندن به BFF، هماهنگ کند.
به عنوان مثال، آهنگ (track) موجودیتی است که دارای اشیاء ارزشی مانند metadata، transcodingها، و سیاستهای مجوز (authorization) برای تعیین دسترسی است. track به user مالک نیز مرتبط است، اما از آنجایی که user موجودیت دیگری است، تنها شامل شناسه کاربر (user ID) به عنوان ارجاع میباشد. سرویس مصرفکننده با داشتن شناسه track، سرویس ارزش افزوده Tracks VAS را فراخوانی میکند؛ VAS از قابلیت دسترسی track اطمینان حاصل میکند و سپس track aggregate مربوطه را برمیگرداند.
در الگوی Backends for Frontends درخواست کاربر نهایی برای پخش track، به BFF ارسال میشود. تعیین اینکه آیا دسترسی کاربر به این track مجاز است یا خیر، بر عهده BFF خواهد بود. این الگو شامل فراخوانی سرویسهای بنیادی مختلفی است که به طور جداگانه مسئول بازگرداندن اطلاعات مجوز و متادیتای track هستند.
با معرفی VAS تمام منطق تکراری حول فراخوانیهای سرویسهای بنیادی در BFFها به سرویس ارزش افزوده Tracks VAS منتقل شد، که علاوه بر کنترل قابلیت دسترسی و مجوزهای track، به ترکیب track aggregate ها برای BFFها نیز رسیدگی میکند. در ادامه BFFمسئول نگاشت track aggregate های داخلی به نمایندگیهای خارجی برای استفاده مشتریان است.
مزایای الگوی VAS
معایب الگوی VAS
ممکن است یک موجودیت (مانند آهنگ - track)، در دامنههای مختلف، برای اهداف مختلف و با الگوهای دسترسی و نیازمندیهای مجوز متفاوتی استفاده شود. به عنوان مثال SoundCloud علاوه بر این که اپلیکیشنی برای فهرست موسیقیها ارائه میدهد، ابزاری را برای سازندگان موسیقی فراهم میکند تا موسیقی خود را آپلود کنند و به اشتراک بگذارند. سازندگان و گوشدهندگان موسیقی، دامنههای متفاوتی هستند.
یکی از روشهای ممکن در اینجا این است که همه موارد مربوط به track(در همه دامنههای مختلف) - از جمله کوئریها و دستورات مرتبط - در یک VAS واحد پیاده سازی شود. این روش برای مدتی به خوبی کار میکند، اما در نهایت خطر افزایش کوپلینگ (coupling) و پیچیدگی و کاهش بهرهوری وجود دارد.
یک رویکرد مقیاس پذیرتر، شناسایی دامنههای مختلف کسبوکار و ایجاد یک درگاه دامنه (Domain Gateway) برای هر یک از آنهاست. در اصل، Domain Gateway اجرای VAS ای است که مربوط به یک دامنه کسبوکار خاص است. هر Domain Gateway میتواند توسط تیمهای مختلف نگهداری شود. به علاوه، هر Domain Gateway دید(view)های متفاوتی را در مورد یک موجودیت معین نشان میدهد. این نما (facade) میتواند پایداری (stability) را فراهم کند و به عنوان یک لایه ضد خرابی (anti-corruption) برای هر یک از دامنهها عمل کند.
مزایای رویکرد Domain Gateway
در این بخش به بررسی تعدادی از ابزارهایی که SoundCloud در معماری خود استفاده کرده است، میپردازیم.
Finagle یک فریمورک RPC توسعهپذیر و مستقل از پروتکل است که توسط Twitter توسعه داده شده است.
اکثر میکروسرویسهای SoundCloud روی Finagleساخته شده اند و به Scala نوشته شده اند.
کلاینتها و سرورهای Finagle با استفاده از non blocking I/O روی فریمورک Netty، باعث افزایش مقیاسپذیری (scalability) میشوند.
Finagle قابلیت پیکربندی و توسعه بالایی دارد.
SoundCloud از یک کتابخانه داخلی به نام jvmkitاستفاده میکند که ساخت میکروسرویسها را برای توسعهدهندگان آسان میسازد. برخی از قابلیتهای jvmkit عبارتند از:
SoundCloud در سال 2016، پلتفرم استقرار بومی خود، Bazooka، را با Kubernetes جایگزین کرد. Kubernetes استقرار، مقیاسبندی و مدیریت containerها را خودکار میکند.
با توجه به این که SoundCloud روی صدها میکروسرویس ساخته شده است، با چالشهای زیادی روبرو است؛ یکی از این چالشها، دیباگ کردن مشکلات سرویسهای درگیر در تکمیل یک درخواست واحد است. به همین علت SoundCloud از ابزار ZipKin استفاده میکند.
ZipKinیک ابزار ردیابی توزیع شده (distributed tracing) است و توسط Twitter پدید آمده است.
علاوه بر ابزارهایی که در بالا بررسی شد، از ابزارهای زیر نیز در معماری SoundCloud استفاده شده است:
پس از مطالعه معماری شرکتها و شناخت ابزارها و فناوریها، آنها را از جنبه ویژگیهای فنی مورد بررسی قرار دادیم.
جدول زیر دستهبندی فناوریها و ابزارهای استفاده شده در Netflix، Amazon و SoundCloud را بر اساس ویژگیهای کیفی نمایش میدهد.
در این پژوهش، ابتدا با مفهوم میکروسرویسها آشنا شدیم و تفاوتهای این سبک معماری را با سبک معماری سرویسگرا دیدیم. پس از آن ویژگیهای مشترک بین میکروسرویسها، سیر تکاملی آنها و ابزارها و فناوریهای موجود برای پیادهسازی میکروسرویسها را با جزئیات بررسی کردیم. همچنین با مطالعه مقالات، برخی از چالشهای عملی در مسیر استفاده از میکروسرویسها و ضدالگوهایی که میتوانند باعث ایجاد یک ساختار معماری ضعیف شوند را مرور کردیم. در ادامه معماری سه شرکت مطرح ، که از میکروسرویسها استفاده میکنند، به نامهای Netflix ، Amazon و SoundCloud را بررسی کرده، دلایل مهاجرت هر یک از آنها به میکروسرویسها، ساختار معماری و ابزارهای مورد استفاده برای توسعه آنها را شرح دادیم. در پایان نیز از جنبهی ویژگیهای کیفی در معماری، به این شرکتها پرداختیم و فناوریها و ابزارهای مورد استفاده هر شرکت، برای تامین ویژگیهای کیفی مورد نیازش را بررسی کردیم.
Alshuqayran, Nuha, Nour Ali, and Roger Evans. "A systematic mapping study in microservice architecture." In 2016 IEEE 9th International Conference on Service-Oriented Computing and Applications (SOCA), pp. 44-51. IEEE, 2016.
Jamshidi, Pooyan, Claus Pahl, Nabor C. Mendonça, James Lewis, and Stefan Tilkov. "Microservices: The journey so far and challenges ahead." IEEE Software 35, no. 3 (2018): 24-35.
Taibi, Davide, and Valentina Lenarduzzi. "On the definition of microservice bad smells." IEEE software 35, no. 3 (2018): 56-62.
https://martinfowler.com/articles/microservices.html
https://www.geeksforgeeks.org/system-design-netflix-a-complete-architecture/
https://medium.com/@narengowda/netflix-system-design-dbec30fede8d
https://dev.to/gbengelebs/netflix-system-design-backend-architecture-10i3
https://thenewstack.io/led-amazon-microservices-architecture/
https://www.codekarle.com/system-design/Amazon-system-design.html
https://relevant.software/blog/microservices-on-aws/
https://blog.thundra.io/microservices-on-aws-an-in-depth-look
https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html
https://blog.dreamfactory.com/microservices-examples/
https://developers.soundcloud.com/blog/category/microservices
https://developers.soundcloud.com/blog/service-architecture-1
https://developers.soundcloud.com/blog/service-architecture-2
https://developers.soundcloud.com/blog/service-architecture-3
https://developers.soundcloud.com/blog/inside-a-soundcloud-microservice
«این مطلب، بخشی از تمرینهای درس معماری نرمافزار در دانشگاه شهیدبهشتی است»