امروزه معماری میکروسرویس به یک معماری پرطرفدار برای اپلیکیشهای بزرگ تبدیل شده و خیلی از شرکتها در حال مهاجرت به سمت این معماری هستند. هدف از این سری پستها بررسی چالشهای پیاده سازی این معماری است با فرض اینکه شما تا حدودی با مفاهیم اولیه این معماری آشنا هستید و از همه مهمتر با تحلیل و بررسی به این نتیجه رسیدید که این معماری میتواند گزینه مناسبی برای اپلیکشن شما باشد.
اگر بخوایم یک تعریف بسیار ابتدایی از این معماری داشته باشیم، باید بگیم که در این معماری یک سری سرویس با ارتباط و تعامل باهم به نیازمندیهای شما پاسخ میدهند شاید این تعریف اصلا تعریف خوبی نباشه مخصوصا از زاویه طراحی و معماری، اما در این تعریف یک نکته وجود داره و اون هم ارتباط میان سرویسها است. خب برای شروع میخوایم بریم سراغ چالشهای موجود برای ارتباط و تعامل سرویسها.
به طور کلی ارتباط میان سرویسها رو میشه از دو زاویه مختلفی بررسی کرد. یکی از زاویه همزمانی و دیگری از زاویه تعداد مخاطبین.
انواع ارتباط از زاویه همزمانی
ارتباط میان سرویسها میتونه به دو صورت همزمان (Synchronous) و غیر همزمان (Asynchronous) صورت بگیره. مثلا ارتباط میان دو نفر میتونه به صورت همزمان و مثلا از طریق تلفن و یا به صورت غیر همزمان و مثلا از طریق نامه صورت بگیره. نکته بسیار مهم در ارتباط همزمان میان کلاینت و سرویس در اینجا است که کلاینت بعد از ارسال درخواست برای مدت زمانی منتظر پاسخ از طرف سرویس میمونه و حتی ممکن هست در این زمان کلاینت بلاک بشه. شاید بهترین مثال برای این نوع ارتباط همین ارتباط میان مرورگر شما با سرور ویرگول برای نمایش این پست باشه.
انواع ارتباط از زاویه تعداد مخاطبین
از زاویه دیگه میشه گفت درخواست میتونه توسط دقیقا یک سرویس (یک به یک) و یا توسط چندیدن سرویس (یک به چند) پردازش بشه.
شاید به نظر نرسه ولی انتخاب نوع ارتباط میان سرویسها یکی از تصمیمهای مهم هنگام طراحی و معماری است و فاکتورهای مختلفی در این انتخاب تاثیر گذار خواهند بود. همچنین باید توجه داشت که این انتخاب میتونه طبعات مختلفی در بخشهای دیگه داشته باشه. هدف این پست اینه که با بررسی چالشها، مزایا و معایب هر کدوم از این روشها، به شما در این انتخاب کمک کنه.
همین الان که شما دارید این پست رو میخونید مرروگر شما از طریق یک ارتباط همزمان با سرور ویرگول، تونسته این متن رو به شما نشون بده. در این ارتباط همزمان از HTTP به عنوان استاندارد و پروتکل گفتوگو استفاده شده. دقیقا همین داستان برای ارتباط سرویسها هم وجود داره و اونها میتونن از طریق یک ارتباط همزمان مثل همین HTTP با هم صحبت کنند. البته که میشه به انواع گوناگونی و با استانداردها و پروتکلهای مختلفی این ارتباط رو برقرار کرد مثل HTTP، gRPC و ... که هر کدوم از این استانداردها و پرتوکلها مزایا و معایب خودشون رو دارند که شاید تو یه پست دیگه به تقضیل درباره اون صحبت کنیم.
خب حالا بریم ببینیم ارتباط همزمان چه چالشهایی رو میتونه برای ما داشته باشه و چه طور میشه با این چالشها روبهرو شد. به طور کلی ارتباط همزمان دو مشکل اساسی ایجاد میکنه : یکی از دسترس خارج شدن موقتی سوریس و دیگری پیدا کردن آدرس سرویس برای ارتباط.
فرض کنید سرور ویرگول خاموش شه آیا شما میتونید این پست رو بخونید ؟؟؟ مسلما نه چون کسی نیست به شما جواب بده و سرور از دسترس خارج شده. همین اتفاق میتونه برای یک سرویس به دلایل مختلفی رخ بده. فرض کنید سرویس B برای انجام کاری نیاز به ارتباط با سرویس A داره و این ارتباط به شکل همزمان پیاده سازی شده. تو یه مقطعی سرویس A از دسترس خارج میشه. ای داد بیداد حالا چه باید کرد !!!
راه حلی که برای این موضوع پیشنهاد میشه دو بخش داره، بخش اول به ما کمک میکنه تا متوجه شیم کی سرویس A دیگه جوابگو نیست و داره بازی درمیاره و بخش دوم به این موضوع میپردازه که حالا که سرویس A در دسترس نیست چه باید کرد.
اولا باید بگم که ممکن هست یک سرویس ۱۰۰ درصد از دسترس خارج نشده باشه ولی انقدر سرش شلوغ هست و درگیره که بهتر بیخیالش بشیم و کار بیشتر سرش نریزیم. در اینجا از دستور العمل شرکت نتفلیکس استفاده میکنیم :
حالا که جوابی از سرویس نمیاد میشه کارهای مختلفی کرد که کاملا بستگی به سناریو موجود داره. سادهترین راه اینه که به کاربر بگیم موقتا سیستم از دسترس خارج شده !!!. البته میشه از روشهایی مثل کش کردن، استفاده از مقدار پیش فرض و یا حتی نادیده گرفتن استفاده کرد. فرض کنید میخواید اطلاعات یک سفارش رو به مشتری نشون بدید و سرویسی که مدیریت ارسال رو بر عهده داره از دسترس خارج شده خب قاعدتا میشه گفت با درصد زیادی هدف این کاربر دیدن آیتمهای سفارش خودش و یا قیمتشون بوده و میشه اصلا قسمت وضعیت ارسال رو بش نشون نداد. همون طور که گفتم این بخش واقعا به سناریو جاری بستگی داره و نمیشه یک فرمول مشخصی برای اون داد.
شما وقتی میخواید به دوستتون زنگ بزنید باید شمارش رو داشته باشید وگرنه بدیهی هستش که نمیتونید زنگ بزنید. دو سرویس هم که میخوان با هم ارتباط داشته باشند باید آدرس IP همدیگر رو داشته باشند تا بتونند با هم حرف بزنند. برای حل این مشکل از یک روش که مثل ۱۱۸ عمل میکنه استفاده میشه که خب جلوتر میبینید که میشه در دو لایه مختلف اون رو پیاده کرد.
در این روش ما چیزی داریم به نام Service Registry که شاید بشه گفت صرفا یک جدول از آدرس IP سرویسهای فعال در اپلیکیشن توش وجود داره. حالا سرویس B وقتی میخواد به سرویس A درخواست بده، نخست از این registry آدرس سرویس A رو میپرسه و اگر اون چیزی بش برگردوند با سرویس A ارتباط میگیره. از طرف دیگه هر سرویس که به اپلیکیشن اضافه میشه باید از طریق یک APIخودش رو به این registry معرفی کنه تا اون مشخصاتش رو ثبت کنه. registry میتونه به صورت دورهای از سلامت سرویسها با خبر بشه و ببینه زنده هستند یا نه !!! و اگر خدایی نکرده مرده باشند اون رو از لیست خودش پاک کنه.
در اینجا بستری که سرویسها روی اون درحال اجرا هستند میتونه به ما کمک کنه. مثلا در kubernetes هنگامی که کانتینر هر کدوم از سرویسها اجرا میشه اون میاد و IP سرویس رو به فهرست خودش اضافه میکنه (میبینید که دیگه نیاز نیست سرویس خودش بیاد و اینکار رو دستی انجام بده). حالا سرویس B به جای گرفتن آدرس سرویس A و ارتباط با اون میاد و درخواست خودش رو به این زیرساخت میفرسته و این زیرساخت هستش که با اطلاعاتی که داره میاد و درخواست رو به سرویس A میرسونه پس دیگه برخلاف روش قبلی سرویسها با واسطه که همون زیرساخت هست باهم صحبت میکنند.