DevOps and Infrastructure

ساخت میکروسرویس‌های مقاوم با Resilience4j و الگوی Circuit Breaker

در فضای سیستم‌های توزیع‌شده مدرن، شکست نه یک مسئله «اگر»، بلکه «کی» است. به عنوان توسعه‌دهندگانی که میکروسرویس‌ها را معماری می‌کنیم، باید بپذیریم که شکاف‌های شبکه، زمان‌های انتظار سرویس و شکست‌های API شخص ثالث اجتناب‌ناپذیرند. اگر یک جزء در زنجیره شما شکست بخورد، می‌تواند به یک اختلال سیستمی منجر شود و کل برنامه شما را از کار بیندازد. اینجاست که الگوهای مقاومت وارد عمل می‌شوند. در میان کتابخانه‌های مختلف موجود برای اکوسیستم جاوا، Resilience4j به عنوان استاندارد طلایی ظهور کرده است و رویکردی سبک و تابعی برای مدیریت خطاها ارائه می‌دهد.

چرا مقاومت در میکروسرویس‌ها مهم است

اپلیکیشن‌های مونولیتیک یک نقطه شکست واحد دارند، اما مدیریت محلی آن‌ها آسان‌تر است. میکروسرویس‌ها، در مقابل، تأخیر شبکه و پیچیدگی را معرفی می‌کنند. وقتی سرویس A به سرویس B تماس می‌گیرد و سرویس B کند یا پاسخگو نیست، رشته‌های (threads) سرویس A می‌توانند مسدود شوند، مخزن رشته‌های آن را مستهلک کنند و در نهایت فراخواننده را از کار بیندازند. به این پدیده «شکست زنجیره‌ای» گفته می‌شود.

مهندسی مقاومت هدفش جلوگیری از این زنجیره‌ها با جداسازی خطاها، تلاش مجدد برای خطاهای گذرا و افتادن به صورت شایسته است. Resilience4j ماژول‌های متعددی برای دستیابی به این هدف ارائه می‌دهد که Circuit Breaker (بریکر مدار) مهم‌ترین آن‌ها برای محافظت در برابر شکست‌های زنجیره‌ای است.

درک الگوی Circuit Breaker

الگوی Circuit Breaker به طور مشابه یک فیوز الکتریکی در خانه شما عمل می‌کند. اگر خطاهای زیادی رخ دهد، فیوز «تریپ» می‌کند و مدار را باز می‌کند، که تمام درخواست‌ها به سرویس شکست‌خورده را متوقف می‌سازد. پس از یک دوره بازیابی مشخص شده، اجازه می‌دهد تعداد محدودی از درخواست‌های «آزمایشی» عبور کنند. اگر این درخواست‌ها موفق باشند، بریکر بسته می‌شود؛ اگر شکست بخورند، دوباره باز می‌شود.

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

پیاده‌سازی Resilience4j در Spring Boot

برای شروع، باید وابستگی Resilience4j Circuit Breaker را به پروژه خود اضافه کنید. اگر از Maven استفاده می‌کنید، موارد زیر را در فایل pom.xml خود قرار دهید:

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>1.7.1</version>
</dependency>
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-circuitbreaker</artifactId>
    <version>1.7.1</version>
</dependency>

پس از اضافه کردن، می‌توانید بریکر مدار را از طریق فایل application.yml خود پیکربندی کنید. این کار پیکربندی را خارجی می‌کند و تنظیم رفتار در محیط تولید را بدون نیاز به استقرار مجدد کد آسان می‌سازد.

resilience4j:
  circuitbreaker:
    instances:
      backendA:
        sliding-window-size: 10
        failure-rate-threshold: 50
        wait-duration-in-open-state: 10s
        permitted-number-of-calls-in-half-open-state: 3

در لایه سرویس خود، به سادگی متدهایی که می‌خواهید محافظت کنید را با انوتیشن مشخص کنید. Resilience4j به طور یکپارچه با AOP (برنامه‌نویسی جهت‌دار جنبه) اسپرینگ و اینترفیس‌های تابعی جاوا ادغام می‌شود.

مثال کد عملی: مکانیزم تلاش مجدد و پاسخ جایگزین

فراتر از صرفاً باز کردن مدار، شما اغلب می‌خواهید درخواست‌های شکست‌خورده را مجدداً تلاش کنید (برای مشکلات شبکه گذرا) و یک پاسخ جایگزین ارائه دهید زمانی که مدار باز است. در اینجا نحوه پیاده‌سازی این مورد با استفاده از انوتیشن‌های Resilience4j در یک سرویس Spring Boot آمده است:

@Service
public class PaymentService {

    private final PaymentClient paymentClient;

    // Constructor injection
    public PaymentService(PaymentClient paymentClient) {
        this.paymentClient = paymentClient;
    }

    @CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")
    @Retry(name = "paymentService")
    public String processPayment(String orderId) {
        // Call external payment gateway
        return paymentClient.charge(orderId);
    }

    // Fallback method signature must match the original method
    public String paymentFallback(String orderId, Exception e) {
        log.error("Payment failed for order: {} due to {}", orderId, e.getMessage());
        return "Payment service currently unavailable. Please try again later.";
    }
}

در این مثال، انوتیشن @Retry با تلاش مجدد برای تماس بر اساس سیاست پیکربندی شده، خطاهای گذرا را مدیریت می‌کند. اگر شکست‌ها ادامه یابد، @CircuitBreaker تریپ می‌کند و متد paymentFallback فراخوانی می‌شود، که تضمین می‌کند کاربر یک پاسخ شایسته دریافت می‌کند نه یک خطای مبهم یا زمان انتظار.

قابلیت مشاهده و پایش

یک بریکر مدار اگر نتوانید وضعیت آن را ببینید، بی‌فایده است. Resilience4j متریک‌ها را از طریق Micrometer در دسترس قرار می‌دهد که با ابزارهایی مانند Prometheus و Grafana ادغام می‌شود. شما می‌توانید متریک‌هایی مانند resilience4j.circuitbreaker.call.fails یا resilience4j.circuitbreaker.state را پایش کنید. این قابلیت مشاهده به شما امکان می‌دهد هشدارهایی برای زمانی که سرویس‌های شما شروع به شکست‌های مکرر می‌کنند تنظیم کنید، که به تیم شما زمان می‌دهد قبل از اینکه مدار کاملاً شکسته شود، موضوع را بررسی کند.

نتیجه‌گیری

ساخت میکروسرویس‌های مقاوم درباره جلوگیری از تمام شکست‌ها نیست، بلکه درباره مدیریت مؤثر آن‌هاست. با بهره‌گیری از Resilience4j، توسعه‌دهندگان جاوا می‌توانند الگوهای مقاومت استاندارد صنعتی مانند Circuit Breakers و Retries را با حداقل کد تکراری پیاده‌سازی کنند. این الگوها از سیستم شما در برابر شکست‌های زنجیره‌ای محافظت می‌کنند، تجربه کاربر را از طریق افت شایسته بهبود می‌بخشند و قابلیت مشاهده لازم برای حفظ سلامت سیستم در محیط تولید را فراهم می‌کنند. همان‌طور که معماری میکروسرویس خود را مقیاس‌بندی می‌کنید، ادغام این استراتژی‌های مقاومت باید در اولویت‌های بالای شما باشد.

Share: