Επισκόπηση Μάθετε πώς να κάνετε τις κλήσεις μικροεξυπηρέτησης ανθεκτικές χρησιμοποιώντας το πρότυπο Circuit Breaker με και Πλήρης επίδειξη, εντολές βήμα προς βήμα, εξηγήσεις κατηγορίας προς κατηγορίας, δειγματοληπτικά αποτελέσματα, πραγματικές περιπτώσεις χρήσης και συμβουλές παραγωγής. Resilience4j Spring Boot Why This Matters Γιατί αυτό έχει σημασία Σε κατανεμημένα συστήματα, μια αποτυχημένη υπηρεσία downstream μπορεί να καταρρεύσει και να προκαλέσει συνολική διακοπή του συστήματος. Το πρότυπο: Circuit Breaker ανιχνεύει μια αποτυχημένη εξάρτηση, Σταματά να στέλνει αιτήματα σε αυτό (για να αποφευχθεί η σπατάλη πόρων), Επιστρέφει μια χαριτωμένη αντίδραση, Περιοδικά δοκιμάζει την εξάρτηση και αποκαθιστά την κανονική λειτουργία όταν είναι υγιής. Αυτό μειώνει τους χρόνους απενεργοποίησης, προστατεύει τις πισίνες νήματος, διατηρεί την εμπειρία του χρήστη λογική και αποτρέπει τις καταιγίδες επαναφοράς. Demo Summary (What You Have) ΔΗΜΟΣΙΟΓΡΑΦΙΚΗ ΣΥΝΟΠΤΙΚΗ (ΤΙ ΕΧΕΙΣ) Ένα δίδυμο υπηρεσία Maven demo: (port ) — simple REST provider that intentionally fails intermittently. hello-service 8081 Τέλος σημείου GET /api/hello (port ) — calls hello-service using RestTemplate and is protected by with a fallback. client-service 8080 Resilience4j @CircuitBreaker Τέλος σημείου GET /api/get-message Και τα δύο ( ) hello-service and client-service Στη συνέχεια το τεστ GET http://localhost:8080/api/get-message Αρχιτεκτονικό διάγραμμα Αυτή είναι μια μικρή, εστιασμένη ροή κατάλληλη για να σχεδιάσετε ένα διάγραμμα Files & Code: Class-By-Class Explanation Αρχεία & Κωδικός: Κατηγορία ανά κατηγορία εξήγηση hello-service ΕΠΙΧΕΙΡΗΣΗ ΕΠΙΧΕΙΡΗΣΗΣ.java Standard bootstrap class. @SpringBootApplication HelloController.java @RestController public class HelloController { private static int counter = 0; @GetMapping("/api/hello") public String sayHello() { counter++; // simulate intermittent failure: fail on every 3rd request if (counter % 3 == 0) { throw new RuntimeException("Simulated failure from Hello-Service!"); } return "Hello from Hello-Service! (count=" + counter + ")"; } } Αυτός ο ελεγκτής ρίχνει σκόπιμα ένα RuntimeException σε περιοδικές κλήσεις για να προσομοιώσει παροδικές αποτυχίες που θα δείτε σε πραγματικά συστήματα (διακοπή DB, κακά δεδομένα, χρονικά διαστήματα). Explanation: client-service ΕΠΙΧΕΙΡΗΜΑΤΙΚΗ ΕΠΙΧΕΙΡΗΣΗ.java Standard Spring Boot main class. Δεν απαιτείται ειδική διαμόρφωση. AppConfig.java @Configuration public class AppConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } Παρέχει ένα μόνο RestTemplate bean. Βεβαιωθείτε ότι το RestTemplate είναι ένα Spring bean, έτσι ώστε οι διαμεσολαβητές AOP/ανθεκτικότητας να μπορούν να λειτουργήσουν σωστά. Explanation: HelloClientService.java @Service public class HelloClientService { private final RestTemplate restTemplate; @Value("${hello.service.url}") private String helloServiceUrl; public HelloClientService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @CircuitBreaker(name = "helloService", fallbackMethod = "fallbackHello") public String getHelloMessage() { System.out.println("Calling hello service: " + helloServiceUrl); return restTemplate.getForObject(helloServiceUrl, String.class); } public String fallbackHello(Throwable t) { System.out.println("Fallback triggered: " + t); return "Hello Service is currently unavailable. Please try again later."; } } Explanation (crucial bits) @CircuitBreaker(name = "helloService", fallbackMethod = "fallbackHello") wraps the getHelloMessage() call in a circuit breaker. The name links to configuration properties. fallbackHello(Throwable t) is called when the call fails according to the breaker rules. The fallback must: o Be in the same class o Have the same return type o Accept the original method parameters (none here) and a final Throwable parameter (or Exception/Throwable compatible with thrown exceptions) Important: The method must be public, and the class must be a Spring bean (@Service), so proxy-based AOP works. ClientController.java @RestController public class ClientController { private final HelloClientService helloClientService; public ClientController(HelloClientService helloClientService) { this.helloClientService = helloClientService; } @GetMapping("/api/get-message") public String getMessage() { return helloClientService.getHelloMessage(); } } Ένας απλός ελεγκτής που εκχωρεί σε Αυτό εξασφαλίζει ότι η κλήση πηγαίνει μέσω του proxy όπου εφαρμόζεται ο διακόπτης κυκλώματος. Explanation: HelloClientService Configuration Used (client-service application.properties) Χρησιμοποιούμενη διαμόρφωση (client-service application.properties) Η βασική διαμόρφωση που χρησιμοποιείται στο demo server.port=8080 spring.application.name=client-service hello.service.url=http://localhost:8081/api/hello resilience4j.circuitbreaker.instances.helloService.registerHealthIndicator=true resilience4j.circuitbreaker.instances.helloService.slidingWindowSize=5 resilience4j.circuitbreaker.instances.helloService.minimumNumberOfCalls=2 resilience4j.circuitbreaker.instances.helloService.failureRateThreshold=50 resilience4j.circuitbreaker.instances.helloService.waitDurationInOpenState=10s logging.level.io.github.resilience4j.circuitbreaker=DEBUG Meaning of important properties SlidingWindowSize: Ο αριθμός των κλήσεων που παρακολουθούν οι διακόπτες για το ποσοστό αποτυχίας. minimumNumberOfCalls: ελάχιστες κλήσεις πριν αξιολογηθεί το ποσοστό αποτυχίας. ποσοστό αποτυχίας: το ποσοστό αποτυχίας (π.χ. 50) για το άνοιγμα του κυκλώματος. waitDurationInOpenState: Πόσο καιρό το κύκλωμα παραμένει ανοιχτό πριν μετακινηθεί σε μισό ανοιχτό. RegistryHealthIndicator: εκθέτει την κατάσταση του διακόπτη μέσω του ενεργοποιητή. Step-by-Step Run & Αναμενόμενες Αποδόσεις Ξεκινήστε τις υπηρεσίες Ξεκινήστε την εξυπηρέτηση Visit: http://localhost:8081/api/hello → «Hello from Hello-Service!» (ή ρίχνει προσομοιωμένη αποτυχία) Returns Ξεκινήστε την εξυπηρέτηση πελατών Visit: http://localhost:8080/api/get-message Test Scenarios & Outputs Σενάρια δοκιμών & outputs Scenario A — Hello-Service Healthy Κάντε αίτηση GET http://localhost:8080/api/get-message Λογισμικό πελάτη Calling hello service: http://localhost:8081/api/hello 2025-11-13T11:58:23.366+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-8] i.g.r.c.i.CircuitBreakerStateMachine : CircuitBreaker 'helloService' succeeded: 2025-11-13T11:58:23.366+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-8] i.g.r.c.i.CircuitBreakerStateMachine : Event SUCCESS published: 2025-11-13T11:58:23.366634+05:30[Asia/Calcutta]: CircuitBreaker 'helloService' recorded a successful call. Elapsed time: 15 ms Response Hello from Hello-Service! (count=4) Scenario B — Hello-Service Intermittent Failures Εάν καλέσετε επανειλημμένα και χαιρετίζετε την υπηρεσία Μερικά από τα αιτήματα: RuntimeException ● Επιτυχημένες κλήσεις: ο πελάτης επιστρέφει το μήνυμα χαιρετισμού. Όταν μια κλήση προς τα κάτω επιστρέφει HTTP 500/εξαίρεση: o Resilience4j records the failure. o If failure rate exceeds threshold (e.g., 50% over sliding window), the Circuit becomes **OPEN**. o While OPEN, calls are short-circuited; **fallbackHello**() is immediately executed — no network call. \n **Client response while fallback active** Client Response while active Response Hello Service is currently unavailable. Please try again later. Sample client log sequence Calling hello service: http://localhost:8081/api/hello 2025-11-13T12:00:55.842+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-1] i.g.r.c.i.CircuitBreakerStateMachine : CircuitBreaker 'helloService' recorded an exception as failure: Scenario C — Recovery Μετά ΔΙΑΡΚΕΙΑ ΑΝΑΚΟΙΝΩΣΗΣ 10 ΣΕ: ΔΙΑΡΚΕΙΑ ΑΝΑΚΟΙΝΩΣΗΣ Το κύκλωμα πηγαίνει στο HALF_OPEN: επιτρέπονται μερικές κλήσεις δοκιμής. Εάν οι κλήσεις δοκιμών είναι επιτυχείς, ο διακόπτης κλείνει και η κανονική κυκλοφορία επαναλαμβάνεται. Εάν οι δοκιμές αποτύχουν, ο διακόπτης επιστρέφει στο OPEN. Real-World Use Cases Πραγματικές περιπτώσεις χρήσης Ενσωμάτωση πληρωμών - Μια υπηρεσία πληρωμής που καλεί ένα εξωτερικό τραπεζικό API: εάν το API της τράπεζας καθυστερήσει / αποτύχει, ο διακόπτης κυκλώματος επιστρέφει ένα φιλικό προς το χρήστη μήνυμα και αποτρέπει τις καταιγίδες επαναφοράς. API με περιορισμένο ρυθμό τρίτου μέρους - API με περιορισμένες κλήσεις ανά δευτερόλεπτο - όταν επιτευχθούν όρια, το κύκλωμα ανοίγει για να αποφευχθεί η περαιτέρω κρούση ποσοστώσεων. Αλυσίδες μικροεξυπηρέτησης μέσα σε μια επιχείρηση - Η υπηρεσία Α καλεί την B, η οποία καλεί την C. Εάν η C είναι ασταθής, οι ανοιχτοί διακόπτες προστατεύουν την B και την A, αποτρέποντας την επιβράδυνση σε όλο το σύστημα. Χαρακτηριστικά μετατοπίσεις και χαριτωμένη υποβάθμιση - Όταν μια μη κρίσιμη υπηρεσία χαρακτηριστικών αποτυγχάνει, επιστρέψτε ένα απλούστερο αποτέλεσμα για να διατηρήσετε τη βασική λειτουργικότητα διαθέσιμη (π.χ. επιστρέψτε τη λίστα προϊόντων χωρίς συστάσεις). Advantages & Business Value Πλεονεκτήματα & Επιχειρηματική Αξία Απομόνωση ελαττωμάτων - αποτρέπει μια κακή εξάρτηση από το να καταρρεύσει. Γρηγορότερη αντίδραση αποτυχίας - αποτυγχάνει γρήγορα αντί να περιμένει για χρονικά διαστήματα. Ευγενική υποβάθμιση - προσφέρει απαντήσεις πτώσης αντί για πλήρεις διακοπές. Προστασία πόρων - Αποφεύγει την σπατάλη CPU, νήματα και δίκτυο. Αυτόματη ανάκαμψη - επιστρέφει αυτόματα στο φυσιολογικό όταν ο εθισμός είναι υγιής. Παρατηρησιμότητα - οι καταστάσεις διακοπής και οι μετρήσεις μπορούν να εκτεθούν μέσω του Ενεργοποιητή και να παρακολουθούνται. Sample logs you’ll see (realistic) Calling hello service: http://localhost:8081/api/hello 2025-11-13T12:00:55.842+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-1] i.g.r.c.i.CircuitBreakerStateMachine : CircuitBreaker 'helloService' recorded an exception as failure 2025-11-13T12:00:55.847+05:30 DEBUG 32692 --- [client-service] [nio-8080-exec-1] i.g.r.c.i.CircuitBreakerStateMachine : Event ERROR published: 2025-11-13T12:00:55.847908200+05:30[Asia/Calcutta]: CircuitBreaker 'helloService' recorded an error: 'org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 : "{"timestamp":"2025-11-13T06:30:55.842+00:00","status":500,"error":"Internal Server Error","path":"/api/hello"}"'. Elapsed time: 8 ms Fallback triggered: org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 : "{"timestamp":"2025-11-13T06:30:55.842+00:00","status":500,"error":"Internal Server Error","path":"/api/hello"}"