وبسایت شخصی حسن هاشمی

برنامه نویس. ایران. قم :))

آشنایی بیشتر با سیستم های توزیع شده، مایکروسرویس ها و پترن های Messageing (بخش اول: مرور صورت مسئله)

توی این پست میخوام یه سری از تجربیات / مطالعات خودم در مورد مایکروسرویس ها، سیستم های توزیع شده و مباحث مربوط بهش به اشتراک بذارم تا هم بدرد شما بخوره هم بعدا خودم بهش ارجاع کنم. اول از همه باید بگم که مبحث مایکروسرویس چیز جدیدی نیست و همه ما برنامه های بزرگمون رو به بخش های کوچیک شکوندیم قبلا، کلمه مایکروسرویس فقط یه کلمه ژیگول برای همونه، اما وقتی یه عده ای جمع میشن و روی یه چیز مشترکا یه اسم انتخاب می کنن باعث میشه که اون اسم و مشتقاتش به مرور استاندارد سازی بشن.

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

یه سری از مشکلات همچین پروژه ها / سولوشن هایی می تونن این موارد باشن:

1- اولین و واضح ترین مشکلش حجم انبوهی از کد هست، کلاس های بزرگ چند ده هزار خطی و پیچیده که عملا Maintenance رو خیلی سخت و هزینه بر می کنه و اضافه کردن برنامه نویس های جدید به تیم رو سخت می کنه بخاطر اینکه آشنا کردنشون به این codebase زمانبر هست.

2- منطقی های غیر مرتبط یه جا تجمیع شدن که، همین باعث میشه گسترش دادن هر کدوم ازین بخش ها به صورت مجزا خیلی سخت باشه. به عنوان یه مثال ساده شما یه وبسایت خبری رو در نظر بگیرید که بخش فیلم هم داره. اگه فایل فیلم همراه با فایل های متون ذخیره سازی و مدیریت بشه، ساده ترین مشکل نگه داری فایل های ایناست چون فیلم غالبا حجیم هستن حالا اگه بخواید سیستم دسترسی امنیتی که اعمال می کنید بین این دو دسته فایل رو جدا کنید معلوم نمیشه که کد این کار باید تو کدوم قسمت باشه اصلا! در نتیجه هر کاری بکنید ختم به خیر نمیشه :)

3- نبود Fault tolerance: برای همین مثال خیلی ساده بالا رو در نظر بگیرید، چه اتفاقی میفته اگه سرویس مدیریت فایل متون به یه علت غیر مرتبط از کار بیفته در اون صورت سرویس مدیریت فایل فیلم هم به صورت خودکار از میفته، در عمل شما به عنوان برنامه نویس قسمت فیلم باید قسمت مقالات رو دیباگ کنید!

4- عدم امکان توزیع مهارت ها: وقتی یه سیستم مجتمع باشه به ناچار تیمی هم که روی اون کار می کنه، باید یکی باشه و اعضای مشترک داشته که در خیلی از موارد اصلا لزومی نداره، حالا البته همین سایز بزرگ تیم هم خیلی از موارد خودش دردسریه.

5- انتشار مستقل: هر بار که میخواید یکی از بخش ها رو روی سرورها آپلود کنید، الزاما باید هر دو زیر سیستم رو با هم آپلود کنید. در صورتیکه شاید یکی ازینا واقعا ورژن خورده باشند.

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

7- مقیاس پذیری مستقل: فرض کنید کارتون گرفت و قسمت فیلم بازدید خیلی زیادی پیدا کرد، و تعداد فایل ها زیاد شد و شما خواستید تعداد سرورهای مرتبط به فیلم رو افزایش بدین، خب به وضوح مجبورید هر دو زیر سیستم رو باهم توزیع کنید که کار اشتباهیه.


خب موارد بالا، ما رو میبره به سمت معماری که به جای اینکه برنامه رو به صورت یه سیستم خیلی بزرگ داشته باشیم، در قالب برنامه های کوچیکی که با هم ارتباط هستند پیاده سازی کنیم.

که هر کدوم ازین زیربرنامه ها رو یه زیرسیستم یا یه سرویس و این الگو رو "مایکروسرویس" میگیم.

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

اما تمرکز این پست روی توضیح روش استاندارد پیاده سازی مایکروسرویس هست.

خب ما الان یه برنامه داریم که معماریش مثل این عکس هست:


و می خوایم برسیم به یه چیزی مثل این:



هدف این هست که هر کدوم ازین سرویس ها کلا مستقل باشند و از کارافتادگی یکیشون باعث از کارافتادگی بقیه نشه، این به این معنی نیست که کلا مستقل باشن چون همچین چیزی اصلا شدنی نیست و با تعریف اولیه هم مخالفه. منظور از استقلال اینه که از کارافتادگی باعث نشه که بقیه سرویس ها stall بشن به طور خلاصه یعنی اینکه از کارافتادگی سرویس های همکار از قبل پیش بینی شده.

پس برای شروع می تونیم بگیم که ما برنامه بزرگمون رو می شکنیم به یه سری زیر برنامه که هر کدوم ازینا یه بخشی از منطق کلی سیستم رو اجرا می کنن و تنها روی یک هدف در دامنه منطق خودشون تمرکز دارن.


این شکستن البته به این سادگی نیست و دو سوال اساسی که مستقل از نحوه پیاده سازی هستن، ایجاد می کنه:

1- شکستن بر چه مبنایی هست؟

2- ارتباط این مایکروسرویس ها چجوری باید باشه که هم تراکنش های منطقی رو تضمین کنه و هم سرعت بالایی داشته باشه چون از دید کاربر بخش عظیمی از منطقی که الان بین سرویس ها قراره اتفاق بیفته قبلا توی مدل monolithic یه فراخوانی محلی بیشتر نبوده و در نتیجه سرعت بالایی داشته.


سوال اول رو میذاریم برای بعد، ولی توی ذهنتون نگه ش دارید. اما برای این که به سوال دوم پاسخ بدیم باید دقت کنیم که ارتباط بین سرویس ها یعنی چی دقیقا.

اول از همه یه وب سرور رو در نظر بگیرید که به عنوان gate way ما عمل می کنه و درخواست ها رو به سرویس انتقال میده

سه سناریو در نظر بگیرید:

(علامت -> یعنی ارسال درخواست)

1- وب سرور -> سرویس:

کاربر درخواستی رو ثبت می کنه طی اون باید یه سری تغییرات توی دیتا اعمال بشه. در صورتی که سرویس متوقف شده کاربر باز هم باید بتونه دیتا رو بفرسته و بعد که سرویس دوباره شروع به کار کرد درخواست کاربر پردازش بشه و به کاربر اطلاع داده بشه.

 یعنی High availability

2- وب سرور -> سرویس -> سرویس -> ... :

توی این حالت یه سری مورد هست که باید توجه بشه، وب سرور درخواست کاربر رو میفرسته به سرویس، و سرویس هم برای انجام عملیاتش نیاز هست که:

     الف) یه دیتایی رو از یه سرویس ثالثی بخونه.

     ب) بعد ازینکه عملیات رو انجام داد یه سری تغییرات رو توی سرویس های دیگه و دیتابیس های دیگه اعمال کنه (مثلا بخواد Replicate کنه یا ..)

توی حالت 'الف' و 'ب' باید تراکنش کلی حفظ بشه، مثلا فرض کنید توی این زنجیره اعمال تغییرات بین سرویس ها یکی از سرویس ها خطا بده (تراکنش)، و بعد ازینکه تمام فراخوانی ها تموم شد یه زنجیره از تغییرات دیگه توی سرویس های دیگه شروع بشه که منطقا ارتباطی به زنجیره اول نداره.

دقت کنید که خیلی مهمه که کاربر در غالب موارد نباید ابدا تا زمانی که این زنجیره های فراخوانی تکمیل بشه منتظر بشه تا response برگرده!! چون همونقدر که برای کاربر بده، به مراتب برای ما بدتره چون دست مارو برای گسترش سیستم به دلایل performance ی می بنده. (احتمالا نمیخواید که کاربر نیم ساعت وایسته تا response شما برگرده)

متن بالا رو هم قرمز کردم و هم underline بیش ازین نمیشه روش استرس کرد :)

در این نقطه من فکر کنم کسایی که بار اولشونه در معرض معماری توزیع شده قرار میگرن یه 10 دقیقه متوقف بشن تا دقیقا منظورم رو از Messaging متوجه بشن.

توی این پست سعی کردم با چیزی که طرف هستیم آشناتون کنم.

توی پست های بعدی اگه فرصت شد انشالله، روی پیاده سازی Microservice / Event Sourcing / CQRS بحث می کنیم.

نظرات (2) -

Loading