From 7aea765dd168c8d1c817cab0f4374f44ffd4de6c Mon Sep 17 00:00:00 2001 From: swarajsaaj Date: Sat, 10 Oct 2020 23:57:53 +0530 Subject: [PATCH] #1510 Fix review comments --- circuit-breaker/README.md | 34 ++++++++++-------- circuit-breaker/etc/ServiceDiagram.PNG | Bin 24371 -> 23591 bytes .../java/com/iluwatar/circuitbreaker/App.java | 3 +- .../circuitbreaker/CircuitBreaker.java | 12 +++---- .../circuitbreaker/DefaultCircuitBreaker.java | 20 ++++++----- .../com/iluwatar/circuitbreaker/AppTest.java | 8 ++++- 6 files changed, 44 insertions(+), 33 deletions(-) diff --git a/circuit-breaker/README.md b/circuit-breaker/README.md index f8c9aca69..f1792cb24 100644 --- a/circuit-breaker/README.md +++ b/circuit-breaker/README.md @@ -19,12 +19,15 @@ cannot bring the whole application down, and we can reconnect to the service as Real world example -> Imagine a web application that has both local files/images and remote database entries to serve. -> The database might not be responding due to a variety of reasons, so if the application keeps -> trying to read from the database using multiple threads/processes, soon all of them will hang -> causing our entire web application will crash. We should be able to detect this situation and show -> the user an appropriate message so that he/she can explore other parts of the app unaffected by -> the database failure. +> Imagine a web application that has both local files/images and remote services that are used for +> fetching data. These remote services may be either healthy and responsive at times, or may become +> slow and unresponsive at some point of time due to variety of reasons. So if one of the remote +> services is slow or not responding successfully, our application will try to fetch response from +> the remote service using multiple threads/processes, soon all of them will hang (also called +> [thread starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science))) causing our entire web application to crash. We should be able to detect +> this situation and show the user an appropriate message so that he/she can explore other parts of +> the app unaffected by the remote service failure. Meanwhile, the other services that are working +> normally, should keep functioning unaffected by this failure. In plain words @@ -65,12 +68,10 @@ public class App { var serverStartTime = System.nanoTime(); var delayedService = new DelayedRemoteService(serverStartTime, 5); - //Set the circuit Breaker parameters var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2, 2000 * 1000 * 1000); var quickService = new QuickRemoteService(); - //Set the circuit Breaker parameters var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2, 2000 * 1000 * 1000); @@ -95,6 +96,7 @@ public class App { //Wait for the delayed service to become responsive try { + LOGGER.info("Waiting for delayed service to become responsive"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); @@ -166,6 +168,7 @@ public class DefaultCircuitBreaker implements CircuitBreaker { private final long retryTimePeriod; private final RemoteService service; long lastFailureTime; + private String lastFailureResponse; int failureCount; private final int failureThreshold; private State state; @@ -195,7 +198,7 @@ public class DefaultCircuitBreaker implements CircuitBreaker { this.failureCount = 0; } - //Reset everything to defaults + // Reset everything to defaults @Override public void recordSuccess() { this.failureCount = 0; @@ -204,12 +207,14 @@ public class DefaultCircuitBreaker implements CircuitBreaker { } @Override - public void recordFailure() { + public void recordFailure(String response) { failureCount = failureCount + 1; this.lastFailureTime = System.nanoTime(); + // Cache the failure response for returning on open state + this.lastFailureResponse = response; } - //Evaluate the current state based on failureThreshold, failureCount and lastFailureTime. + // Evaluate the current state based on failureThreshold, failureCount and lastFailureTime. protected void evaluateState() { if (failureCount >= failureThreshold) { //Then something is wrong with remote service if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { @@ -263,8 +268,8 @@ public class DefaultCircuitBreaker implements CircuitBreaker { public String attemptRequest() throws RemoteServiceException { evaluateState(); if (state == State.OPEN) { - // return cached response if no the circuit is in OPEN state - return "This is stale response from API"; + // return cached response if the circuit is in OPEN state + return this.lastFailureResponse; } else { // Make the API request if the circuit is not OPEN try { @@ -276,13 +281,12 @@ public class DefaultCircuitBreaker implements CircuitBreaker { recordSuccess(); return response; } catch (RemoteServiceException ex) { - recordFailure(); + recordFailure(ex.getMessage()); throw ex; } } } } - ``` How does the above pattern prevent failures? Let's understand via this finite state machine diff --git a/circuit-breaker/etc/ServiceDiagram.PNG b/circuit-breaker/etc/ServiceDiagram.PNG index 661f1a1058181b689b0724418e598f7b5b245c31..885320a4d9014d18f071638d7075e6987c463fd6 100644 GIT binary patch literal 23591 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 diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java index 0a88e4e5a..302930585 100644 --- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java @@ -69,12 +69,10 @@ public class App { var serverStartTime = System.nanoTime(); var delayedService = new DelayedRemoteService(serverStartTime, 5); - //Set the circuit Breaker parameters var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2, 2000 * 1000 * 1000); var quickService = new QuickRemoteService(); - //Set the circuit Breaker parameters var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2, 2000 * 1000 * 1000); @@ -99,6 +97,7 @@ public class App { //Wait for the delayed service to become responsive try { + LOGGER.info("Waiting for delayed service to become responsive"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java index b05e72104..92884258b 100644 --- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java @@ -28,18 +28,18 @@ package com.iluwatar.circuitbreaker; */ public interface CircuitBreaker { - //Success response. Reset everything to defaults + // Success response. Reset everything to defaults void recordSuccess(); - //Failure response. Handle accordingly and change state if required. - void recordFailure(); + // Failure response. Handle accordingly with response and change state if required. + void recordFailure(String response); - //Get the current state of circuit breaker + // Get the current state of circuit breaker String getState(); - //Set the specific state manually. + // Set the specific state manually. void setState(State state); - //Attempt to fetch response from the remote service. + // Attempt to fetch response from the remote service. String attemptRequest() throws RemoteServiceException; } diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java index 1d48c142a..6b2f401d5 100644 --- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java @@ -24,9 +24,8 @@ package com.iluwatar.circuitbreaker; /** - * The delay based Circuit breaker implementation that works in a - * CLOSED->OPEN-(retry_time_period)->HALF_OPEN->CLOSED flow with some retry time period for failed - * services and a failure threshold for service to open + * The delay based Circuit breaker implementation that works in a CLOSED->OPEN-(retry_time_period)->HALF_OPEN->CLOSED + * flow with some retry time period for failed services and a failure threshold for service to open * circuit. */ public class DefaultCircuitBreaker implements CircuitBreaker { @@ -35,6 +34,7 @@ public class DefaultCircuitBreaker implements CircuitBreaker { private final long retryTimePeriod; private final RemoteService service; long lastFailureTime; + private String lastFailureResponse; int failureCount; private final int failureThreshold; private State state; @@ -64,7 +64,7 @@ public class DefaultCircuitBreaker implements CircuitBreaker { this.failureCount = 0; } - //Reset everything to defaults + // Reset everything to defaults @Override public void recordSuccess() { this.failureCount = 0; @@ -73,12 +73,14 @@ public class DefaultCircuitBreaker implements CircuitBreaker { } @Override - public void recordFailure() { + public void recordFailure(String response) { failureCount = failureCount + 1; this.lastFailureTime = System.nanoTime(); + // Cache the failure response for returning on open state + this.lastFailureResponse = response; } - //Evaluate the current state based on failureThreshold, failureCount and lastFailureTime. + // Evaluate the current state based on failureThreshold, failureCount and lastFailureTime. protected void evaluateState() { if (failureCount >= failureThreshold) { //Then something is wrong with remote service if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { @@ -132,8 +134,8 @@ public class DefaultCircuitBreaker implements CircuitBreaker { public String attemptRequest() throws RemoteServiceException { evaluateState(); if (state == State.OPEN) { - // return cached response if no the circuit is in OPEN state - return "This is stale response from API"; + // return cached response if the circuit is in OPEN state + return this.lastFailureResponse; } else { // Make the API request if the circuit is not OPEN try { @@ -145,7 +147,7 @@ public class DefaultCircuitBreaker implements CircuitBreaker { recordSuccess(); return response; } catch (RemoteServiceException ex) { - recordFailure(); + recordFailure(ex.getMessage()); throw ex; } } diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java index 3b4041103..d28d316c8 100644 --- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java +++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java @@ -27,12 +27,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * App Test showing usage of circuit breaker. */ public class AppTest { + private static final Logger LOGGER = LoggerFactory.getLogger(AppTest.class); + //Startup delay for delayed service (in seconds) private static final int STARTUP_DELAY = 4; @@ -79,7 +83,7 @@ public class AppTest { //As failure threshold is "1", the circuit breaker is changed to OPEN assertEquals("OPEN", delayedServiceCircuitBreaker.getState()); //As circuit state is OPEN, we expect a quick fallback response from circuit breaker. - assertEquals("This is stale response from API", monitoringService.delayedServiceResponse()); + assertEquals("Delayed service is down", monitoringService.delayedServiceResponse()); //Meanwhile, the quick service is responding and the circuit state is CLOSED assertEquals("Quick Service is working", monitoringService.quickServiceResponse()); @@ -96,6 +100,7 @@ public class AppTest { //Waiting for recovery period of 2 seconds for circuit breaker to retry service. try { + LOGGER.info("Waiting 2s for delayed service to become responsive"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); @@ -114,6 +119,7 @@ public class AppTest { //Waiting for 4 seconds, which is enough for DelayedService to become healthy and respond successfully. try { + LOGGER.info("Waiting 4s for delayed service to become responsive"); Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace();