Within a microservices architecture, a circuit breaker serves as a design pattern to effectively manage failures and mitigate the risk of cascading failures caused by a failed microservice. Like an electrical circuit breaker, this pattern is designed to “trip” and halt further execution of requests when a certain threshold of failures is reached. This prevents the system from persistently calling a failed service, giving ample time for the failed service to recover.
The circuit breaker pattern has three main states, as discussed below:
When in the closed state, the circuit breaker permits the passage of requests to the underlying component. It monitors the component’s performance, such as the failure rate or response times. If the monitored metrics are within acceptable limits, the circuit remains closed, and requests are processed as usual.
When the monitored metrics, such as the failure rate, exceed a predefined threshold, the circuit breaker transitions to the open state. In the open state, the circuit breaker does not allow requests to pass through to the failing component. Instead of making calls likely to fail, the system may take alternative actions, such as returning cached data, providing default values, or invoking a fallback mechanism. The open state helps prevent further stress on the failing component, giving it time to recover.
After a specified time in the open state, the circuit breaker shifts to the half-open state. In the half-open state, a limited number of requests are allowed to pass through to the underlying component to check if it has recovered. If these test requests are processed successfully, the circuit breaker returns to the closed state and normal operation resumes. If the test requests continue to fail, the circuit breaker may remain in the open state, providing more time for the underlying component to recover before allowing full traffic.
Each state of a circuit breaker is depicted in the following figure:
In a microservices architecture, the circuit breaker pattern is typically applied at the boundary between microservices or when a service calls an external dependency (e.g., another microservice, a database, or a third-party API). Placing the circuit breaker strategically helps isolate and contain failures, preventing them from cascading throughout the system. The following are common scenarios to place the circuit breaker in a microservices architecture:
Around external service calls: Apply the circuit breaker pattern around calls to external services or dependencies. This ensures that if an external service experiences issues, the circuit breaker can open, and the system can gracefully handle the failure without causing a cascade of failures in the calling service.
Between microservices: Place the circuit breaker at the point where one microservice calls another microservice. This is particularly important in a microservices architecture where services communicate over a network. The circuit breaker helps prevent failures in one microservice from affecting others.
Before critical operations: Apply the circuit breaker before critical or resource-intensive operations. For example, if a microservice relies on a database, a circuit breaker can be placed around database calls to handle failures or excessive response times.
At the edge of the system: Consider placing circuit breakers at the edge of the system, where external requests enter. This can help protect the overall system from failures originating from external clients.
Let’s demonstrate the working of a circuit breaker using Python programming language. The following code wraps a call to simulated service with a circuit breaker. At the end, the code also shows the current status of the circuit breaker, as shown below:
from pybreaker import CircuitBreakerimport httpxdef service_call():# This function simulate a successful service callprint("The similulation to call a service is completed, successfully.")# Create a Circuit Breakerbreaker = CircuitBreaker(fail_max=3, reset_timeout=10)try:with httpx.Client() as client:# Wrap the call to the service with the circuit breakerbreaker.call(client.get, "https://example.com")service_call()print("Service is called successfully.")except Exception as e:print(f"Oh no, the service call failed: {e}")# Circuit breaker statusprint("The current status of the circuit breaker is:", breaker.current_state)
Line 1: Import the required modules and classes from the pybreaker
library. CircuitBreaker
is the main class for creating a circuit breaker. It also imports the httpx
module for making HTTP requests.
Line 4–6: Define a function service_call()
that simulates a successful external service call.
Line 9: Create an instance of the CircuitBreaker
class named breaker. Here, we also configure the circuit breaker with a maximum failure count of 3 (fail_max=3
) and a reset timeout of 10 seconds (reset_timeout=10
).
Line 11–20: Use a try
block to encapsulate the code involving an external service call.
Line 23: Print the current status of the circuit breaker.
In essence, a circuit breaker is a pattern that safeguards a microservice architecture from cascading failures. When a failure occurs, or a service goes down, it halts the traffic until the system recovers. Primarily, its three states—open, closed, and half-open—play a crucial role in determining the next course of action.
Free Resources