در معماریهای نرمافزاری، همانند مدارهای الکترونیک، مفهومی داریم به نام "قطع کن". این مفهوم از دنیای الکتریک وارد معماریهای نرمافزاری شده است.
حتما فیوزهای مینیاتوری را در سیستمهای برقکشی ساختمانی دیدید. کار این فیوزها که عمدتا در جعبه تقسیم قرار دارند، جهت قطع کردن مدار در زمان به وجود آمدن یک اتصالی در سیمکشی داخلی، یا به عبارت دقیقتر، زمانی که میزان مصرف بیشتر از آستانه این فیوز است. مثلا یخچالها که مصرف بیشتری دارند، به فیوزهای 25 آمپر و روشناییها که مصرف کمتری دارند، به فیوزهای 16 آمپر مجهز میکنند. کل برق یک واحد را هم به فیوزهای 32 آمپر تجهیز میکنند.
جالب این است که دقیقا همین الگو رو در معماریهای نرمافزاری هم داریم. در این نوشته مختصری در خصوص این موضوع توضیح خواهیم داد. قبل از ورود به این الگو باید مفهوم IO را بشناسیم.
برای نوشتن یک نرمافزار مفاهیم بیرونی متعددی بجز برنامه نوشته شده، دست به دست هم میدهند تا در نهایت یک امکان فراهم شود. بهعنوان مثال، بانک اطلاعاتی در اکثر نرمافزارها وجود دارد که دادههای کارکردی یا موجودیتهای یک نرمافزار در آن نگهداری میشود. (این بانک اطلاعاتی، بر حسب نیاز، میتواند اشکال و مدلهای مختلفی داشته باشد: relational, key-value, ... )
در واقع عملیات IO به هر رویدادی گفته میشود که از مسیر اصلی برنامه یا main thread برنامه خارج شود و عموما این اصل برای IO در برنامهنویسی حاکم است که IO یک عملیات زمانبر است. لیست زیر انواع IO است:
فرض کنید یک سیستم نرمافزاری داریم که برای انجام کارش به زیرسامانهها و ماژولهای مختلفی شکسته شده و هر کدام از این ماژولها هم در معماری میکروسرویس از چندین میکروسرویس استفاده میکنند.
میکروسرویسی رو فرض کنیم که کارش این است که یک استعلام از یک سرویس بگیرد (فرض کنیم یک سرویس احراز هویت را باید مثلا از ثبت احوال صدا بزنیم یا قرار است یک پیامک بفرستیم یا قرار است یک ایمیل ارسال کنیم).
این میکروسرویس یک از کارهایی است که در یک سرویس بالادستی (مثلا سرویس ثبت سفارش) انجام شده و آن سرویس ثبت سفارش توالی زیر را انجام میدهد:
فرض کنیم این سرویس، 100 بار در ثانیه صدا زده میشود و قرار است طبق SLA توافق شده در 5 ثانیه به سرویس بالادستیاش جواب برگرداند. این یعنی باید میکروسرویس احراز هویت و میکروسرویس پیامک، حداکثر در 3 ثانیه جواب سرویس ثبت سفارش را برگردانند. همچنین 2 ثانیه هم، خود سرویس ثبت سفارش برای سایر کارهایش نیازمند زمان است.
حالا شرایطی رو فرض کنیم که سرویس احراز هویت یا پیامک قطع شده یا ارتباط با آن برقرار نیست. در این حالت چه اتفاقی میافتد؟
در این حالت اغلب خطاهایی در سطح شبکه اتفاق خواهد افتاد که عمدتا خطای Connection time out است و خطا در لایه شبکه معمولا حدود چندین ثانیه طول میکشد که در شرایط مختلف این زمان متغیر است. (از 15 ثانیه به بالا) این زمان بهگونهای است که SLA سرویس زیر پا گذاشته خواهد شد و سرویس بالادستی (سرویس ثبت سفارش) هم تحت تاثیر این شرایط قرار خواهد گرفت و در زمان مورد نظرش امکان پاسخدهی نخواهد داشت.
در این حالت thread ای که سرویس ثبت سفارش رو صدا زده باید باز بماند تا زمانی که سرویس احراز هویت به time out رسیده و به اصلاح خطای مورد نظرش را برگرداند. پس تبعات زیر را متصور هستیم:
همانطور که میدانیم هر thread باز و هر اتصال TCP باز، از سیستمعامل، منابع خودش را میگیرد و در حالتی که این قطعی اتفاق میافتد منابع سیستمعامل (CPU و RAM و Network) صرف شده تا در نهایت time out اتفاق افتاده و طولانی شدن این شرایط منجر به halt شدن سیستم خواهد شد.
راهکار برای این شرایط استفاده از الگوی طراحی Circuit Breaker است که نقش آن حفظ کردن SLA تعریف شده برای یک سرویس است.
Circuit Breaker یک قطعه برنامه یا ماژول است که در معماریهای میکروسرویس بهکار گرفته میشود (بهدلیل تعدد dependency که در این معماری وجود دارد، اهمیت بهکارگیری این الگو، بیش از پیش است. هر چند امکان استفاده از آن در هر معماری وجود دارد) که در مسیر (مدار) عملیات IO مستقر شده و بر اساس پارامترهای تعریف شده، اقدام به باز کردن مدار (قطع کردن ارتباط بیرونی) و بستن مدار (برقرار کردن ارتباط) مینماید.
این الگو معمولا برای بازکردن مدار 3 پارامتر را در نظر دارد:
به کارگیری این الگو در وضعیت بحران، شرایط زیر را به وجود خواهد آورد:
ما در آویهنگ از این الگوی معماری، در پروژههای مختلف، زمانی که سرویسهای بیرونی (محیط external) را فراخوانی میکنیم، به کرات استفاده کردیم و این باعث شده تا از مشکلاتی که در زمان قطعی برای کل معماری رخ میدهد، جلوگیری شود.