محمد سپهر ملائی
محمد سپهر ملائی
خواندن ۷ دقیقه·۲ سال پیش

مقدمه ای بر میکروسرویس ها

در توسعه نرم افزار، ایده اصلی معماری میکروسرویس بر تجزیه عملکردی مبتنی است. به بیان ساده تر به جای توسعه یک برنامه بزرگ، آن را در قالب سرویس های جداگانه توسعه داده و با هم ادغام می کنیم. در صورت درک اولیه این مفهوم، ممکنه با ابهامات متعددی روبرو بشیم از جمله اینکه تعریف از سرویس چیست؟ اندازه یک سرویس چه قدر است؟ تعامل بین آنها چگونه انجام می شود؟ و ...

برای پاسخ به این ابهامات لازمه که ابتدا با معماری نرم افزار آشنا بشیم. Chris Richardson در کتاب Microservices Patterns به این صورت تعریف مورد علاقه خودش رو از معماری نرم افزار بیان می کنه:

معماری نرم افزار مجموعه ای از ساختارهای مورد نیاز برای موجودیتی است که شامل عناصر نرم افزاری، روابط بین آنها و ویژگی های هرکدام می باشد.

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

  • پیچیدگی کمتر و قابلیت نگهداری بیشتر
  • به طور مستقل قابل استقرار
  • توسعه راحتتر و انعطاف پذیری بیشتر
  • تحمل خطای بالاتر
  • استقلال تیم های توسعه برای هر سرویس
  • امکان استفاده از ابزار های توسعه مختلف (زبان های برنامه نویسی و فریم وورک ها)

اندازه یک میکروسرویس چه قدر باشد؟

برای پاسخ به این ابهام ابتدا باید منظور خودمون رو از اندازه مشخص کنیم. آیا تعداد خط های کد می تواند معیار صحیحی برای اندازه باشه؟ به نظر نمی رسه که بتونه معیار درستی باشه در نظر بگیرید یک پیاده سازی ساده اگر برای Java تعداد 25 خط کد نیاز داشته باشه در Clojure به سادگی در 10 خط انجام میشه.

سم نیومن در کتاب Building Microservices نقل قول جالبی از James Lewis بیان کرده:

یک میکروسرویس باید اندازه کَله من باشه

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


تعامل بین سرویس ها چگونه انجام میشود؟

هدف نهایی تهیه واسط برنامه نویسی نرم افزار (API) می باشد. به گونه ای که سایر سرویس ها با استفاده از آن بتوانند تعامل مناسبی با سرویس کنونی برقرار کنند. در ادامه برخی از مهمترین رویکرد هایی که برای تعامل بین سرویس ها استفاده می شود خدمتتون بیان میکنیم.

1. REST

انتقال بازنمودی حالت (Representational State Transfer) نوعی معماری نرم افزار است که مجموعه اصول و قواعدی را برای بهره وری از یک سرویس خاص در بستر وب فراهم می کند.

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

بعضا دیده می شود که REST و HTTP به جای یکدیگر به کار برده می شوند. دقت کنید که HTTP در حقیقت یک پروتکل از لایه کاربرد شبکه است که امکان تبادل داده میان کلاینت و سرور را میسر می کند. در صورتی که REST مجموعه اصول و قواعدی را بیان می کند که برای معماری نرم افزار به کار برده می شود. یک سیستم RESTful، برای افزایش انعطاف پذیری از قواعد خاص HTTP شامل GET, POST, DELETE استفاده می کند. یادآوری این نکته مهم است که استفاده از پروتکل HTTP برای یک سیستم RESTful لازم نیست.

2. GraphQL

یکی از ایراداتی که به REST گرفته شد تعداد حالت های محدود برای نمایش داده است. برای توضیح بیشتر به مورد زیر توجه کنید:

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

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

3. Message brokers

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

Response registerUser(User user) { Response response = userService.register(user); messageSender.send(response.getMessage()); return response; }

واضح است که در این مثال ابتدا کاربری را در سامانه ثبت نام کرده سپس پیامی را (از طریق ایمیل، پیامک و ...) برای او ارسال می کنیم و در نهایت نتیجه نهایی را برای کاربر برمیگردانیم. نکته مهم در این مثال ارسال پیام است. اگر بخواهیم فراخوانی send از messageSender را به صورت سنکرون در نظر بگیریم در این صورت باید تا پایان فرایند ارسال پیام به کاربر منتظر مانده و سپس نتیجه را به او بازگشت دهیم. آیا برای برنامه سمت کاربر بهینه است که تا پایان ارسال پیام منتظر بماند تا در نهایت نتیجه را دریافت کند؟

در مدل آسنکرون این مشکل برطرف میشود و با فراخوانی send دیگر تا پایان آن منتظر نمی مانیم و نتیجه را بلافاصله به برنامه سمت کاربر برمیگردانیم.

برای پیاده سازی فراخوانی آسنکرون معمولا به این صورت عمل می شود که هر سرویس پیام (Message) را برای دیگر سرویس ها می فرستد. پیام یک مفهوم عمومی است که می‌تواند شامل درخواست، پاسخ یا رویدادی باشد که میان سرویس ها رد و بدل می شود. این پیام توسط فرستنده به کارگزار پیام (Message broker) ارسال می‌شود که در نهایت گیرنده آنها را از کارگزار دریافت می کند. کارگزاران برای مدیریت پیام ها ممکنه که از topic، queue و یا هردو استفاده کنند.

صف
صف

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

تاپیک
تاپیک

تاپیک topic: امکان اشتراک گذاری پیام میان چندین سرویس را فراهم می کند. هر مصرف کننده در حقیقت یک کپی از پیام داخل تاپیک را دریافت میکند. این مفهوم به طور گسترده ای در Apache Kafka استفاده می شود.

ممکنه تا حالا از خودتون پرسیده باشین که اصلا کارگزار پیام چه مزیتی داره؟ چرا پیام ما مستقیم برای سرویس هدف ارسال نمی شه؟ برای رفع این ابهامات توجهتون رو به مزایای این کارگزاران جلب می کنیم:

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

در خصوص ارتباط از طریق کارگزاران پیام، یکی از مهمترین مفاهیمی که مطرح هست الگوهای ادغام سازمانی (Enterprise integration patterns) هست که بیانگر مجموعه ای از الگوهای طراحی است که برای ادغام نرم افزار های موجود در یک سیستم تجاری استفاده می شود. این الگوها بر رویکرد ارتباط از طریق ارسال پیام، میانِ سرویس ها اشاره می کنند. در قسمت بعد سعی میکنیم بیشتر آن را بررسی کنیم.


منابع:

Microservices Patterns by Chris Richardson

Building Microservices, 2nd Edition by Sam Newman


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