ali.bayat
ali.bayat
خواندن ۱۷ دقیقه·۳ سال پیش

واکشی داده‌ها در معماری مایکرو سرویس


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

این وابستگی باعث می‌شود تا مایکرو سرویس ها بیشتر نیاز به تغییر داشته باشند، توسعه آنها نیاز به تلاش بیشتر داشته باشد و انعطاف پذیری کمتری در پروداکشن داشته باشند.

تکثیر انتخابی داده ها (Selective data replication) به شما امکان می دهد از داده های مایکرو سرویس دیگری استفاده کنید و در عین حال دامنه وابستگی را به حداقل برسانید. مایکرو سرویس هایی که از این رویکرد استفاده می‌کنند، متکی به اسکیمای وابستگی‌های خود (و پشتیبانی از تکثیر داده ها) هستند. پس آنها به وبسرویس خارجی سایر مایکرو سرویس ها اتکا ندارند. این امر هزینه های وابستگی در توسعه و پروداکشن را به حداقل می رساند.

مقدمه

در حین توسعه اپلیکیشن‌های یکپارچه (Monolithic) مسلما میدانیم که اجرای کوئری روی یک تک دیتابیس رابطه‌ای چقدر آسان است.

اما در استفاده از معماری مایکرو سرویس ماجرا کمی فرق دارد، داده های شما در دیتابیس های مختلف توزیع می‌شوند؛ و هر مایکرو سرویس تنها می‌تواند به دیتابیس خودش دسترسی داشته باشد. همچنین هیچ راه حل آماده ای برای پیوند دادن داده (Data Joining) های چند مایکرو سرویس وجود ندارد.

در این نوشته برخی از رویکردها را برای کوئری زدن و پیوند داده ها بین چند مایکرو سرویس بررسی خواهیم کرد



تکثیر انتخابی داده ها یا Selective Data Replication

در این روش داده‌های مورد نیاز را از مایکرو سرویس‌ های دیگر، در دیتابیس مایکرو سرویس مورد نظر رِپلیکیت می‌کنیم. در نتیجه تنها اتصال یا بهتر است بگوییم وابستگی بین سرویس ها، تنظیمات رپلیکیشن داده هاست.

استفاده از روش تکثیر با الگوی Publish Subscribe را در نظر بگیرید; که منجر به ایجاد Loose-Coupling بین اسکیمای دیتابیس ها می‌شود. که این Loose-Coupling تا حدی ما را در مقابل تغییرات وابستگی ها، عایق می‌کند.

طبق تعریف ویکی پدیا‌ ، سیستمی Loosely-Coupled است که:

۱) اجزای (کامپوننت ها) آن با یکدیگر ارتباط ضعیفی دارند، پس تغییرات یک جزء کمترین تأثیر را بر وجود یا عملکرد جزء دیگر می گذارد.
۲) اجزای آن آگاهی ای نسبت به تعاریف سایر اجزاء ندارند (یا آگاهی بسیار اندکی دارند) . تعاریغی مانند: کلاس ها، اینترفیس ها، داده ها و سرویس ها.


مزایا :

پیاده سازی را ساده می کند: روشی یک دست برای اجرای کوئری ها فراهم می‌شود. (نیازی نیست با استفاده از REST کوئری های سرویس های مختلف را ترکیب کرد) .... همچنین میتوان از متدهای Native موجود در دیتابیس بهره برد; مانند SQL Joins یا map-reduce

تست کردن را ساده می‌کند: می توان برای تکثیر داده ها جدا از عملکرد مایکرو سرویس تست نوشت. همچنین در حین تست کردن یک مایکرو سرویس، نیازی نیست سایر مایکرو سرویس ها آنلاین باشند.

انعطاف بیشتر: برای اجرای کوئری و جمع آوری داده ها نیازی نیست سایر مایکرو سرویس ها آنلاین باشند (مادامی که داده ها تکثیر شده باشند).

تفکیک موارد (Separation of concerns): ادغام داده ها با سایر سرویس ها کاملا از پیاده سازی وب سرویس مجزا شده است.

کوئری های داخلی بخشی از وب سرویس های ریموت نیستند: از آنجایی که دسترسی به داده‌ها بین مایکرو سرویس‌ ها از طریق تکثیر داده‌ها انجام می‌شود، لازم نیست این کوئری‌ها بخشی از وب سرویس ریموت شما (به عنوان مثال REST) ​​باشند.

ظوابط کوئری توسط مایکرو سرویس مصرف کننده داده اعمال می شود: برای ارائه یک وب سرویس ریموت با ظوابطی که نیاز دارید; دیگر به مایکرو سرویسی که داده ها را ارائه می کند، وابسته نیستید. مایکرو سرویس های مصرف کننده کوئری های خود را (با هر ظوابطی که نیاز دارند) پیاده سازی می کنند.

در نتیجه زمانی که الزامات مایکرو سرویس های مصرف کننده تغییر می کند، نیازی به تغییرات در مایکرو سرویس ارائه دهنده نیست

تأخیر کمتر: به جای تماس با سایر مایکرو سرویس ها، می توان داده ها را مستقیماً از دیتابیس دریافت کرد.

از عملیات I/O غیر ضروری در کوئری جلوگیری می‌شود: وب سرویس های REST پیاده شده در مایکرو سروی ها، معمولا موجودیت‌های کامل را بر می‌گردانند. اما با استفاده از روش تکثیر می‌توان تنها اطلاعات مورد نیاز را واکشی کرد.

مقاومت بیشتر در برابر حمله Denial-of-Service: مایکرو سرویسی که تحت حمله قرار می‌گیرد، اضافه باری برای سایر مایکرو سرویس ها ایجاد نمی‌کند.


معایب :

تمام دیتابیس ها از رپلیکیشن پشتیبانی نمی‌کنند: حتی برای یک سیستم مدیریت دیتابیس مشخص ممکن است در نسخه های مختلف دچار محدودیت شویم.

ممکن است برخی سرویس های database-as-a-service پشتیبانی نشوند: ممکن است برای تکثیر لحظه ای داده ها، نیاز به تغییر در تنظیمات و یا نصب برخی اکستنشن ها باشد.

ریسک عدم هماهنگی داده ها: پروسه تکثیر داده ها باید مانیتور شود; در صورت بروز هر گونه خطا در این پروسه، باید مشکلات همگام سازی را قبل از اینکه روی مشتریان شما تأثیر بگذارد، شناسایی و رفع کنید. پروسه تکثیر Eventually-Consistent یا نهایتا پایدار خواهد بود اما به ازای هر تغییر بازه زمانی کوتاهی وجود دارد که داده ها بین مایکرو سرویس ها کاملا همگام نیستند.

یادگیری فنی بیشتری لازم است: به غیر از تکنولوژی هایی که با آنها آشنا هستید، تکثیر داده ها فناوری هایی را معرفی می کند که معمولا اکثر توسعه دهندگان با آن آشنایی ندارند.

بخش های دیگری برای پیکربندی، اجرا، تست و نگهداری اضافه می‌شوند: باید در تمام سطوح برنامه را اجرا و تست کنید (از توسعه تا پروداکشن)

یک مرحله اضافه به پروسه استقرار (Deployment) اپلیکیشن اضافه می‌شود: پس از تغییرات در اسکیمای دیتابیس، ممکن است لازم باشد تا پیکربندی تکثیر داده ها نیز به روز شود.

تلاش برای راه اندازی اولیه: در بین پروسه های فراگیری تکنولوژی جدید و اعمال تنظیمات لازم، برای تکثیر داده ها تلاش معقولی لازم است.

سربار داده های تکراری: هرچه داده های بیشتری بین سرویس ها تکثیر شود، هزینه های نگهداری و پشتیبان گیری از دیتابیس بیشتر می شود.

نگرانی های مربوط به امنیت داده ها و حفظ حریم خصوصی افزایش می‌یابند: شما کپی های تکراری از داده های ذخیره شده در چندین دیتابیس دارید که احتمالاً این داده ها در سرویس پیام رسان (Messaging Service) شما نیز موجود هستند. این رویکرد ممکن است برای اطلاعات حساسی (مانند جزئیات کارت اعتباری) مناسب نباشد.



استفاده از الگوی API Composition

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

مزایا :

مایکرو سرویس های سطح پایین وابستگی به یکدیگر ندارند: این نکته در تئوری این باعث می شود که مایکرو سرویس ها پایدارتر، عمومی تر و قابل استفاده مجدد باشند.

داده های لایو: داده هایی که دریافت می کنید حاوی وضعیت فعلی مایکرو سرویس ارائه شده اند; هر چند که هنوز هم باید با Eventual-consistency و عدم ایزوله شدن تراکنش های بین مایکرو سرویس ها مقابله کنید.

یا کار می کند یا کار نمی‌کند: اگر خطایی رخ دهد، آن خطا مرتبط با یک درخواست خاص است. و این امر شناسایی و حل آن خطا را آسان‌تر می‌کند. استک تِریس ها در بین مایکرو سرویس ها منتقل نمی‌شوند; برای سهولت دسترسی به علت اصلی می‌توان از ردیابی توزیع شده (Distributed Tracing) استفاده کرد. برای آشنایی بیشتر با این مفهوم، می‌توانید به OpenTracing مراجعه کنید.

پشتیبانی از تولید کد: می‌توانید مشخصات وب سرویس ریموت خود را به طور کلی تعریف کنید (به عنوان مثال در OpenAPI) و سپس می‌توانید کد مدل، سرور و کلاینت را بر اساس آن تولید کنید.


معایب‌ :

اگر مایکرو سرویس سطح پایین نیاز به استفاده داخلی از داده ها داشته باشد، این رویکرد قابل پیاده سازی نیست: انتقال داده های خاص پیاده سازی موجود در درخواست، به مایکرو سرویس های سطح پایین وسوسه انگیز است. اما انجام این کار کپسوله سازی وب‌سرویس را از بین برده و ضعف در تفکیک موارد (Separation of Concerns) را نمایان می‌کند.

سرویس های مرکب وابستگی زیادی به مایکرو سرویس های زیر بنایی دارند: شما نقاط زیادی را در کدهایتان دارید که در هم تنیده اند (Tightly-Coupled) یا به عبارتی وابستگی تنگا تنگی دارند... حال شما آن در هم تنیدگی ها را به سرویس های مرکب انتقال داده‌اید. به این ترتیب ممکن است تعداد مایکرو سرویس های در هم تنیده را کاهش داده باشید اما سرویس های مرکب را به گلوگاهی برای اعمال تغییرات تبدیل کرده‌اید.

انسجام ضعیف: برای یک اِندپوینت مشخص در وب سرویس، مقداری کد در سرویس مرکب وجود دارد که حداقل بین ۲ مایکرو سرویس سطح پایین پخش می شوند. پس برای پیاده سازی هر اندپوینت ممکن است برای ۳ بار (یا بیشتر) لازم باشد این مراحل را طی کنید: پول ریکوئست (Pull Request)، کد رویو (Code Review)، انتشار (Release) و استقرار (Deployment) ... با تغییر در سرویس های مرکب، برخی از اندپوینت های وب سرویس (در مایکرو سرویس های سطح پایین) ممکن است دیگر مورد نیاز نباشند. از آنجا که شناسایی این اندپوینت ها کمی دشوار است، اغلب در کد باقی مانده و به طور غیر ضروری نگهداری می شوند.

سرویس های ریموت برای پرفرمنس بهتر نیاز به انبوهی از کوئری ها دارند: اگر چندین ردیف داده را در بین مایکرو سرویس ها با هم جوین (Join) کنید، با مشکل n+1 مواجه خواهید شد. مشکل n+1 جایی رخ می‌دهد که هر سطر در نتایج به یک کوئری اضافی (در این مورد به مایکرو سرویس دیگر) منجر می‌شود. یک راه حل برای این مشکل استفاده از یک وب سرویس برای کوئری های انبوه است، بنابراین می توانید داده های اضافی را برای همه ردیف ها به طور همزمان واکشی کنید.

نوشتن Join ها به صورت دستی (Handwritten Joins): نمی‌توانید از دیتابیس بهره ببرید تا جوین ها را برای شما انجام دهد، در عوض باید جوین ها و تست های مربوط به آنها را خودتان بنویسید; که ممکن است منجر به ایجاد باگ و یا مشکلات پرفرمنسی شود.

نیاز به آنلاین بودن وابستگی ها دارد: هر مایکرو سرویس برای اینکه بتواند کار کند، نیاز به آنلاین بودن وابستگی های خود دارد; آفلاین شدن یک مایکرو سرویس می‌تواند مانند یک دومینو منجر به آفلاین شدن دیگر سرویس ها شود. همچنین سرویس مرکب لایه دیگری است که باید آنلاین باشد. در این حالت حتی برای تست کردن سیستم به صورت لوکال هم، به اجرای وابستگی ها نیاز دارید. ممکن است بتوانید به جای مایکرو سرویس های واقعی از شبیه سازها استفاده کنید; اما شبیه سازها اغلب به آن اندازه ای متفاوت هستند که مشکل ایجاد کنند. در صورت امکان، باید تست های یکپارچه سازی را در مقابل مایکرو سرویس های واقعی آزمایش کنید; به دلیل وابستگی های گذرا ممکن است مجبور شوید برای توسعه و یا تست، بیشتر مایکرو سرویس های خود را اجرا کنید.

کوئری هایی بیش از آنچه نیاز دارید: بسیار معمول است که برای واکشی تنها نام یک موجودیت، کل آن موجودیت را از دیتابیس واکشی می‌کنیم، که منجر به هدر رفتن منابع I/O می‌شود.

تاخیر بیشتر: یک لایه اضافه در شبکه حاصل می‌شود که هر درخواست باید از آن عبور کند. هر پرش اضافی در شبکه تاخیر قابل توجهی به پردازش درخواست اولیه اضافه می‌کند.

یک وب سرویس دیگر برای توسعه و نگهداری دارید: این رویکرد آنچه را که می‌توانست یک وب سرویس داخلی (در یک مایکرو سرویس) باشد به یک وب سرویس ریموت (بین دو لایه مایکرو سرویس) تبدیل می‌کند.

خصوصیات تا حد زیادی تکراری هستند: سرویس مرکب بخش‌های بزرگی از وب سرویس های مایکرو سرویس‌ های سطح پایین را کپی می‌کند. که به احتمال زیاد این قسمت ها نیاز به همگام سازی دارند.

اشکال زدایی درخواست ها دشوار تر است: دیباگرها نمی‌توانند از لایه های مایکرو سرویس عبور کنند.

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

جزء دیگری برای نسخه گذاری، انتشار و استقرار: تغییرات وب‌سرویس اغلب شامل تغییر در سرویس مرکب و مایکرو سرویس های سطح پایین می شود.


این رویکرد معمولاً به تلاش بیشتری نسبت به سایر روش ها نیاز دارد. اینکه آیا مشکل درهم تنیدگی مایکرو سرویس را کاهش می دهد یا فقط آن را به نقطه ای جدید جابجا می کند قابل بحث است.



الگوی CQRS (Command Query Responsibility Segregation)

اگر بخواهیم نام این الگو را ترجمه کنیم به چنین عبارتی می‌رسیم: تفکیک مسئولیت پرسش و فرمان. (پرسش و فرمان... یا خواندن و نوشتن)

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

توضیحات کامل در مورد این الگو در این نوشته نخواهد گنجید (احتمالا به زودی مطلبی جداگانه درباره این موضوع منتشر خواهم کرد.)، اما به طور مختصر این الگو به ۳ حالت کلی قابل انجام است; استفاده از ۱ دیتابیس، استفاده از ۲ دیتابیس و استفاده از Event-Sourcing... که هر کدام از این روش ها نیز نکات مثبت و منفی خود را دارند... که ما در اینجا یک حالت کلی را در نظر می‌گیریم.

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


مزایا :

مقیاس پذیری مستقل: در برنامه‌های معمول، اکثرا بخش خواندن، بیشتر از نوشتن استفاده می‌شود و کاربران معمولا اطلاعات را دریافت و می‌بینند تا اینکه در آن تغییری ایجاد کنند؛ در این صورت شما می‌توانید بخش خواندن برنامه‌ی خود را Scale کرده و تعداد سیستم یا منابع بیشتری را به این قسمت از برنامه‌ی خود اختصاص دهید. ( Horizontal Scaling & Vertical Scaling )

انتخاب تکنولوژی های بهینه: جدا کردن پروسه نوشتن از خواندن به شما امکان می‌دهد تا از بهترین تکنولوژی دیتابیس استفاده کنید، به عنوان مثال، پایگاه داده SQL برای نوشتن و پایگاه داده غیر NoSQL برای خواندن.

بهره بردن از استراتژی جغرافیایی: از آنجا که معمولا خواندن داده ها بیشتر از نوشتن آنها ست، بنابراین می‌توانید با قرار دادن منابع خواندن در مکان‌های جغرافیایی استراتژیک، پرفرمنس را افزایش و تأخیر در پاسخ را کاهش دهید.

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

پایبندی به SOLID: با تقسیم وظایف بهتر می‌توانیم به اصل تک مسئولیتی (Single Responsibility) پایبند باشیم

تفکیک موارد (Separation of concerns): از آنجایی که دو مدل مجزا برای خواندن و نوشتن داریم، می‌توانید اعتبارسنجی ها و منطق های پیچیده را داخل مدل نوشتن پیاده سازی کنید و مدل های مربوط به خواندن را برای واکشی داده ها کاملا ساده نگه دارید.


معایب‌ :

پیچیدگی بیشتر: با وجود این جداسازی بین خواندن و نوشتن، پیچیدگی نرم افزار زیاد می شود.

نیاز به تخصص در دیتابیس های بیشتر: علاوه بر دانشی که برای استفاده بهینه از دیتابیس های مختلف لازم است، همچنین هزینه های سخت افزاری نگهداری دیتابیس های بیشتر و یا هزینه نگهداری روی Cloud افزایش می‌یابد.

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

سازگاری داده ها: اطمینان حاصل کردن از سازگاری داده ها مستلزم توجه ویژه است (به تئوری سی‌ای‌پی مراجعه کنید).



ویوهای بین اسکیمای دیتابیس

در این رویکرد، به جای دیتابیس های کاملاً مجزا، هر مایکرو سرویس اسکیمایی جداگانه در همان دیتابیس دارد. و از ویو ها برای اشتراک داده ها در بین اسکیما ها استفاده می‌شود.

این استراتژی یک رویکرد خوب برای مهاجرت از معماری یکپارچه به معماری مایکرو سرویس است. همچنین مهاجرت از این رویکرد به رویکرد تکثیر داده ها نسبتاً آسان است.


مزایا :

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

داده ها نمی‌توانند از همگام سازی خارج شوند: همه چیز در یک پایگاه داده است و داده ها فقط یک بار ذخیره می‌شوند.

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


معایب :

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

اسکیما های دیتابیس به شدت به یکدیگر وابسته هستند: شما متکی به دریافت داده ها از یک اسکیمای خاص و در نتیجه محدود به ساختار آن اسکیما هستید.

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

یک دیتابیس بزرگ، تست یکپارچه سازی را کندتر می‌کند: پروسه بازسازی/تنظیم مجدد دیتابیس بین تست ها بیشتر طول می کشد.

یک سیستم مدیریت دیتابیس واحد: برخی از مایکرو سرویس ها کاندید اجرا و پیاده سازی در دیتابیس رابطه ای هستند و برخی دیگر در دیتابیس های سند-محور بهتر عمل می‌کنند... در این حالت شما محدود به استفاده از یک سیستم مدیریت دیتابیس هستید.. (هر چند دیتابیس هایی داریم که مالتی مدل هستند، مانند آرانگو دیبی که در کنار جوین ها، مدل داده سند و همچنین گراف را نیز ارائه می‌کنند.)



جوین کردن داده ها در رابط کاربری (UI)

در این رویکرد، هنگامی که رابط کاربری نمی‌تواند تمام داده های مورد نیاز خود را از یک اندپوینت دریافت کند، UI برای دریافت داده های مرتبط، اندپوینت های اضافی را فراخوانی می‌کند. این روش برای داده های مرجع به خوبی کار می کند.


مزایا :

مایکرو سرویس ها به هم وابستگی ندارند: هر چند که این وابستگی به لایه UI منتقل شده است.

داده های لایو: داده هایی که دریافت می کنید حاوی وضعیت فعلی مایکرو سرویس ارائه شده اند; هر چند که هنوز هم باید با Eventual-consistency و عدم ایزوله شدن تراکنش های بین مایکرو سرویس ها مقابله کنید.

پشتیبانی از تولید کد: می‌توانید مشخصات وب سرویس ریموت خود را به طور کلی تعریف کنید (به عنوان مثال در OpenAPI) و سپس می‌توانید کد مدل، سرور و کلاینت را بر اساس آن تولید کنید.

استفاده از کش در لایه HTTP: چنانچه از داده های مرجع نسبتا ثابت استفاده می کنید، می‌توانید از کش کردن داده ها در این قسمت هم بهره ببرید.


معایب :

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

سرویس های ریموت برای پرفرمنس بهتر نیاز به انبوهی از کوئری ها دارند: اگر چندین ردیف داده را در بین مایکرو سرویس ها با هم جوین (Join) کنید، با مشکل n+1 مواجه خواهید شد. مشکل n+1 جایی رخ می‌دهد که هر سطر در نتایج به یک کوئری اضافی (در این مورد به مایکرو سرویس دیگر) منجر می‌شود. یک راه حل برای این مشکل استفاده از یک وب سرویس برای کوئری های انبوه است، بنابراین می توانید داده های اضافی را برای همه ردیف ها به طور همزمان واکشی کنید.

نوشتن Join ها به صورت دستی (Handwritten Joins): نمی‌توانید از دیتابیس بهره ببرید تا جوین ها را برای شما انجام دهد، در عوض باید جوین ها و تست های مربوط به آنها را خودتان بنویسید; که ممکن است منجر به ایجاد باگ و یا مشکلات پرفرمنسی شود.

تأخیر بالاتر: تماس‌ها از فرانت‌اند به بک‌اند تأخیر بیشتری نسبت به تماس‌ های بین مایکرو سرویس‌ ها دارند.



کوئری های مستقیم بین مایکرو سرویس ها

در این رویکرد (که پیشنهاد نمی‌شود) هر مایکرو سرویس در صورت نیاز، وب سرویس سایر مایکرو سرویس ها را فراخوانی می‌کند.

این انتخاب، انتخابی غریزی برای توسعه دهندگانی است که با اپلیکیشن های یکپارچه کار می‌کنند. آنها تماس های محلی را با وب‌سرویس های ریموت جابجا می‌کنند.


مزایا :

داده های لایو: داده هایی که دریافت می کنید حاوی وضعیت فعلی مایکرو سرویس ارائه شده اند; هر چند که هنوز هم باید با Eventual-consistency و عدم ایزوله شدن تراکنش های بین مایکرو سرویس ها مقابله کنید.

یا کار می کند یا کار نمی‌کند: اگر خطایی رخ دهد، آن خطا مرتبط با یک درخواست خاص است. و این امر شناسایی و حل آن خطا را آسان‌تر می‌کند. استک تِریس ها در بین مایکرو سرویس ها منتقل نمی‌شوند; برای سهولت دسترسی به علت اصلی می‌توان از ردیابی توزیع شده (Distributed Tracing) استفاده کرد. برای آشنایی بیشتر با این مفهوم، می‌توانید به OpenTracing مراجعه کنید.

پشتیبانی از تولید کد: می‌توانید مشخصات وب سرویس ریموت خود را به طور کلی تعریف کنید (به عنوان مثال در OpenAPI) و سپس می‌توانید کد مدل، سرور و کلاینت را بر اساس آن تولید کنید.


معایب :

مایکرو سرویس ها وابستگی زیادی به یکدیگر دارند: با توجه به آنچه گفته شد، این امر بسیاری از مزایای استفاده از مایکرو سرویس ها را تضعیف می کند.

سرویس های ریموت برای پرفرمنس بهتر نیاز به انبوهی از کوئری ها دارند: اگر چندین ردیف داده را در بین مایکرو سرویس ها با هم جوین (Join) کنید، با مشکل n+1 مواجه خواهید شد. مشکل n+1 جایی رخ می‌دهد که هر سطر در نتایج به یک کوئری اضافی (در این مورد به مایکرو سرویس دیگر) منجر می‌شود. یک راه حل برای این مشکل استفاده از یک وب سرویس برای کوئری های انبوه است، بنابراین می توانید داده های اضافی را برای همه ردیف ها به طور همزمان واکشی کنید.

نوشتن Join ها به صورت دستی (Handwritten Joins): نمی‌توانید از دیتابیس بهره ببرید تا جوین ها را برای شما انجام دهد، در عوض باید جوین ها و تست های مربوط به آنها را خودتان بنویسید; که ممکن است منجر به ایجاد باگ و یا مشکلات پرفرمنسی شود.

نیاز به آنلاین بودن وابستگی ها دارد: هر مایکرو سرویس برای اینکه بتواند کار کند، نیاز به آنلاین بودن وابستگی های خود دارد; آفلاین شدن یک مایکرو سرویس می‌تواند مانند یک دومینو منجر به آفلاین شدن دیگر سرویس ها شود. همچنین سرویس مرکب لایه دیگری است که باید آنلاین باشد. در این حالت حتی برای تست کردن سیستم به صورت لوکال هم، به اجرای وابستگی ها نیاز دارید. ممکن است بتوانید به جای مایکرو سرویس های واقعی از شبیه سازها استفاده کنید; اما شبیه سازها اغلب به آن اندازه ای متفاوت هستند که مشکل ایجاد کنند. در صورت امکان، باید تست های یکپارچه سازی را در مقابل مایکرو سرویس های واقعی آزمایش کنید; به دلیل وابستگی های گذرا ممکن است مجبور شوید برای توسعه و یا تست، بیشتر مایکرو سرویس های خود را اجرا کنید.

کوئری هایی بیش از آنچه نیاز دارید: بسیار معمول است که برای واکشی تنها نام یک موجودیت، کل آن موجودیت را از دیتابیس واکشی می‌کنیم، که منجر به هدر رفتن منابع I/O می‌شود.

تاخیر بیشتر: یک لایه اضافه در شبکه حاصل می‌شود که هر درخواست باید از آن عبور کند. هر پرش اضافی در شبکه تاخیر قابل توجهی به پردازش درخواست اولیه اضافه می‌کند.



Loose CouplingApi CompositionCQRS PatternmicroservicesQueries
توسعه دهنده ارشد وب
شاید از این پست‌ها خوشتان بیاید