Skip to content

Commit

Permalink
Fix for issue #413: Circuit Breaker Pattern (#986)
Browse files Browse the repository at this point in the history
* Fix Issue #413: Circuit-Breaker Pattern

* Fix Image Links

* Remove Javadoc plugin to ensure correct build

* Implementing code review feedback

* Sync README with actual code
  • Loading branch information
PalAditya authored and iluwatar committed Oct 14, 2019
1 parent 088bb76 commit 7e698a9
Show file tree
Hide file tree
Showing 13 changed files with 772 additions and 0 deletions.
188 changes: 188 additions & 0 deletions circuit-breaker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
---
layout: pattern
title: CircuitBreaker
folder: circuit-breaker
permalink: /patterns/circuit-breaker/
categories: Other
tags:
- Java
- Performance
- Difficulty-Intermediate
---

## Intent

Handle costly remote *procedure/service* calls in such a way that the failure of a **single** service/component cannot bring the whole application down, and we can reconnect to the service as soon as possible.

## Explanation

Real world example

> Imagine a Web App that has both local (example: files and images) and remote (example: 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 and 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 without any problem.
In plain words

> Allows us to save resources when we know a remote service failed. Useful when all parts of our application are highly decoupled from each other, and failure of one component doesn't mean the other parts will stop working.
Wikipedia says

> **Circuit breaker** is a design pattern used in modern software development. It is used to detect failures and encapsulates the logic of preventing a failure from constantly recurring, during maintenance, temporary external system failure or unexpected system difficulties.
So, how does this all come together?

## Programmatic Example
With the above example in mind we will imitate the functionality in a simple manner. We have two services: A *monitoring service* which will mimic the web app and will make both **local** and **remote** calls.

The service architecture is as follows:

![alt text](./etc/ServiceDiagram.PNG "Service Diagram")

In terms of code, the End user application is:

```java
public class App {

private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

public static void main(String[] args) {
var obj = new MonitoringService();
var circuitBreaker = new CircuitBreaker(3000, 1, 2000 * 1000 * 1000);
var serverStartTime = System.nanoTime();
while (true) {
LOGGER.info(obj.localResourceResponse());
LOGGER.info(obj.remoteResourceResponse(circuitBreaker, serverStartTime));
LOGGER.info(circuitBreaker.getState());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
}
}
}
}
```

The monitoring service is:

``` java
public class MonitoringService {

public String localResourceResponse() {
return "Local Service is working";
}

public String remoteResourceResponse(CircuitBreaker circuitBreaker, long serverStartTime) {
try {
return circuitBreaker.call("delayedService", serverStartTime);
} catch (Exception e) {
return e.getMessage();
}
}
}
```
As it can be seen, it does the call to get local resources directly, but it wraps the call to remote (costly) service in a circuit breaker object, which prevents faults as follows:

```java
public class CircuitBreaker {
private final long timeout;
private final long retryTimePeriod;
long lastFailureTime;
int failureCount;
private final int failureThreshold;
private State state;
private final long futureTime = 1000 * 1000 * 1000 * 1000;

CircuitBreaker(long timeout, int failureThreshold, long retryTimePeriod) {
this.state = State.CLOSED;
this.failureThreshold = failureThreshold;
this.timeout = timeout;
this.retryTimePeriod = retryTimePeriod;
this.lastFailureTime = System.nanoTime() + futureTime;
this.failureCount = 0;
}

private void reset() {
this.failureCount = 0;
this.lastFailureTime = System.nanoTime() + futureTime;
this.state = State.CLOSED;
}

private void recordFailure() {
failureCount = failureCount + 1;
this.lastFailureTime = System.nanoTime();
}

protected void setState() {
if (failureCount > failureThreshold) {
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
state = State.HALF_OPEN;
} else {
state = State.OPEN;
}
} else {
state = State.CLOSED;
}
}

public String getState() {
return state.name();
}

public void setStateForBypass(State state) {
this.state = state;
}

public String call(String serviceToCall, long serverStartTime) throws Exception {
setState();
if (state == State.OPEN) {
return "This is stale response from API";
} else {
if (serviceToCall.equals("delayedService")) {
var delayedService = new DelayedService(20);
var response = delayedService.response(serverStartTime);
if (response.split(" ")[3].equals("working")) {
reset();
return response;
} else {
recordFailure();
throw new Exception("Remote service not responding");
}
} else {
throw new Exception("Unknown Service Name");
}
}
}
}
```

How does the above pattern prevent failures? Let's understand via this finite state machine implemented by it.

![alt text](./etc/StateDiagram.PNG "State Diagram")

- We initialize the Circuit Breaker object with certain parameters: **timeout**, **failureThreshold** and **retryTimePeriod** which help determine how resilient the API is.
- Initially, we are in the **closed** state and the remote call to API happens.
- Every time the call succeeds, we reset the state to as it was in the beginning.
- If the number of failures cross a certain threshold, we move to the **open** state, which acts just like an open circuit and prevents remote service calls from being made, thus saving resources. (Here, we return the response called ```stale response from API```)
- Once we exceed the retry timeout period, we move to the **half-open** state and make another call to the remote service again to check if the service is working so that we can serve fresh content. A *failure* sets it back to **open** state and another attempt is made after retry timeout period, while a *success* sets it to **closed** state so that everything starts working normally again.


## Applicability
Use the Circuit Breaker pattern when

- Building a fault-tolerant application where failure of some services shouldn't bring the entire application down.
- Building an continuously incremental/continuous delivery application, as some of it's components can be upgraded without shutting it down entirely.

## Related Patterns

- [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry)

## Real world examples
* [Spring Circuit Breaker module](https://spring.io/guides/gs/circuit-breaker)
* [Netflix Hystrix API](https://github.com/Netflix/Hystrix)

## Credits

* [Understanding Circuit Breaker Pattern](https://itnext.io/understand-circuitbreaker-design-pattern-with-simple-practical-example-92a752615b42)
* [Martin Fowler on Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html)
* [Fault tolerance in a high volume, distributed system](https://medium.com/netflix-techblog/fault-tolerance-in-a-high-volume-distributed-system-91ab4faae74a)
* [Microsoft docs](https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker)
Binary file added circuit-breaker/etc/ServiceDiagram.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added circuit-breaker/etc/StateDiagram.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions circuit-breaker/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!--
The MIT License
Copyright (c) 2014 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.22.0-SNAPSHOT</version>
</parent>
<artifactId>circuit-breaker</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
86 changes: 86 additions & 0 deletions circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.iluwatar.circuitbreaker;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* <p>
* The intention of the Circuit Builder pattern is to handle remote failures
* robustly, which is to mean that if a service is dependant on n number of
* other services, and m of them fail, we should be able to recover from that
* failure by ensuring that the user can still use the services that are actually
* functional, and resources are not tied up by uselessly by the services which
* are not working. However, we should also be able to detect when any of the m
* failing services become operational again, so that we can use it
* </p>
* <p>
* In this example, the circuit breaker pattern is demonstrated by using two services:
* {@link MonitoringService} and {@link DelayedService}. The monitoring service
* is responsible for calling two services: a local service and a remote service {@link DelayedService}
* , and by using the circuit breaker construction we ensure that if the call to
* remote service is going to fail, we are going to save our resources and not make the
* function call at all, by wrapping our call to the remote service in the circuit
* breaker object.
* </p>
* <p>
* This works as follows: The {@link CircuitBreaker} object can be in one of three
* states: <b>Open</b>, <b>Closed</b> and <b>Half-Open</b>, which represents the real
* world circuits. If the state is closed (initial), we assume everything is alright
* and perform the function call. However, every time the call fails, we note it
* and once it crosses a threshold, we set the state to Open, preventing any further
* calls to the remote server. Then, after a certain retry period (during which we
* expect thee service to recover), we make another call to the remote server and
* this state is called the Half-Open state, where it stays till the service is down,
* and once it recovers, it goes back to the closed state and the cycle continues.
* </p>
*/
public class App {

private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
//Create an object of monitoring service which makes both local and remote calls
var obj = new MonitoringService();
//Set the circuit Breaker parameters
var circuitBreaker = new CircuitBreaker(3000, 1, 2000 * 1000 * 1000);
var serverStartTime = System.nanoTime();
while (true) {
LOGGER.info(obj.localResourceResponse());
LOGGER.info(obj.remoteResourceResponse(circuitBreaker, serverStartTime));
LOGGER.info(circuitBreaker.getState());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
}
}
}
}
Loading

0 comments on commit 7e698a9

Please sign in to comment.