Circuit breaker in microservices

Circuit breakers are a design pattern to create resilient microservices by limiting the impact of service failures and latencies. In this blog, we demonstrate how to incorporate a circuit breaker into microservices to ensure that a system remains responsive and available even in the event of failures or unexpected loads.

GraphQL has a role beyond API Query Language- being the backbone of application Integration
background Coditation

Circuit breaker in microservices

Before we talk about Circuit breakers', let’s briefly look at microservice architecture and in which scenario it is frequently used.

What is Microservice Architecture?

In the microservice architecture, web applications are developed by dividing their functionality into separate, autonomous services that work together to deliver seamless and responsive performance. These services are developed and deployed individually and are loosely coupled, in contrast to the interdependent components of a monolithic application.

Let's take the example of “Spotify”. It requires many different services, so each service deals with a specific task. For example, a search engine, user behavioral analytics for a recommendation engine, autogenerated playlists, content tagging, and so on. So each of the services is built separately and communicates with each other.

Learn more about Microservice architecture here

What is the circuit breaker pattern?

The circuit breaker pattern is used to prevent downstream failures in distributed systems. This design pattern has been extensively used to address the problem of downstream failures.

It helps prevent a cascading failure across a distributed system, by allowing a microservice to temporarily "trip" its circuit and enter a fallback state when it starts to experience issues. This fallback state could be to stop serving requests, to serve a default or cached response, or to redirect requests to a different instance of the service. Circuit breakers are used to protect the overall system from overloading or failing due to a single service that is experiencing issues. They can help to improve the resilience and stability of a microservices architecture.

A circuit breaker can be in one of the following states:

  • Closed: This is the circuit breaker's normal state, in which it allows requests to pass through to the external dependency.
  • Open: The circuit breaker has detected a failure in the external dependency and prevents requests from being sent to it in this state. Rather, depending on the implementation, it returns a default response or throws an exception.
  • Half-open: In this state, the circuit breaker allows a limited number of requests to pass through to the external dependency to test its availability. If the requests are given, the circuit breaker returns to its closed position. If the requests fail, the circuit breaker returns to its open position.

Circuit Breakers vs. Timeouts for Microservices

We now know what the purpose of a circuit breaker is. Timeouts can be used in the same way to accomplish the same purpose for cascading services. Microservices use timeouts to limit the amount of time they will wait for a response from downstream services. If the downstream service does not respond within the specified timeout period, the microservice will terminate the request and return an error to the caller. Timeouts are typically used to protect a microservice from getting stuck waiting for a response from a downstream service that is taking too long to respond.

In summary, circuit breakers are used to protect a microservice from making requests to a downstream service that is failing, while timeouts are used to protect a microservice from getting stuck waiting for a response from a downstream service. Both mechanisms can be used to improve the reliability and resiliency of a microservice architecture.


The 5 Benefits of Using Circuit Breakers

  • Fault tolerance: Circuit breakers prevent a microservice from repeatedly attempting to make requests to a failing downstream service, which can improve the system's overall fault tolerance.
  • Reduced load on downstream services: Circuit breakers can help reduce the load on a failing downstream service by preventing a microservice from making requests to it. This can help prevent the service from becoming further overloaded or failing.
  • Enhanced reliability: Circuit breakers can help improve a microservice's reliability by preventing it from becoming stuck waiting for a response from a downstream service that is failing or responding slowly.
  • Faster corrective actions: Circuit breakers can aid in the detection of failures by tripping as soon as the downstream service begins to fail or respond slowly. This can help the system respond to failures and take corrective action more quickly.
  • Improved user experience: Circuit breakers can help improve the overall user experience by reducing the frequency of errors and downtime by improving the fault tolerance and reliability of a microservice.

The purpose and concept of circuit breakers are now clear to us, so let's put them into practice.

We will be using “Golang” and the "gobreaker" (https://github.com/sony/gobreaker) package to implement a circuit breaker in Go, which provides a simple and easy-to-use implementation of the circuit breaker pattern.

Initialize the new circuit breaker with a timeout and a threshold of 5 failures. After 5 failures circuit breaker will block all requests unless it gets successful requests. If we get five unsuccessful attempts, it will trip the circuit breaker and change the state to open.


var cb *gobreaker.CircuitBreaker

func init() {

   cb = gobreaker.NewCircuitBreaker(gobreaker.Settings{
       Name:    "circuit-breaker",
       Timeout: time.Millisecond,
       ReadyToTrip: func(counts gobreaker.Counts) bool {
           return counts.Requests >= 5
       },
       OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {
           fmt.Printf("%s -- %s\n", from, to)
       },
   })
}

We need to write the GetData function, which will hit the API with the circuit breaker object.


//  HTTP GET request to the server
func GetData(url string) error {
   _, err := cb.Execute(func() (interface{}, error) {
       response, err := http.Get(url)
       if err != nil {
           return []byte{}, err
       }
       defer response.Body.Close()
       // Read the response body
       data, err := ioutil.ReadAll(response.Body)
       if err != nil {
           return []byte{}, err
       }
       return data, nil
   })
   return err
}

And finally, the main function is to call the APIs. We have one correct URL and another incorrect URL for the sake of tripping the circuit breaker. We'll call the API 10 times, and when the counter reaches 5, we'll use the correct API.


func main() {
    url: = ""
    correctURL: = "http://localhost:3000"
    incorrectURL: = "http://localhost:8080"
    var err error
    url = incorrectURL
    for i: = 0;i < 10;i++{
        err = GetData(url)
        if err != nil {
            fmt.Println("Data Fetch Failure")
        } else {
            fmt.Println("Data Fetch Success")
        }
        if i > 5 {
            url = correctURL
        }
        time.Sleep(time.Second)
    }
}

O/P :


Data Fetch Failure
Data Fetch Failure
Data Fetch Failure
Data Fetch Failure
closed -- open
Data Fetch Failure
open -- half-open
half-open -- open
Data Fetch Failure
open -- half-open
half-open -- open
Data Fetch Failure
open -- half-open
half-open -- closed
Data Fetch Success
Data Fetch Success
Data Fetch Success


As we can see, the first four attempts are made, and on the fifth attempt, the circuit state is changed from closed to open. And then circuit breaker will then retry to fulfill the request, and if successful, it will mark the request as "open" to "half-open" and “half-open” to "open,"  and once we have enough successful requests, it will mark “half-open” to "closed."

Conclusion:

The circuit-breaker pattern can aid in the reliability of microservice architectures. It avoids making unnecessary calls to underlying services when they are unavailable, which improves overall system stability. Incorporating a circuit breaker into your microservices can help to ensure that your system remains responsive and available even in the event of failures or unexpected loads.

Hello, I am Akshay Navale, a highly driven and passionate software developer, avid learner, and tech enthusiast, always striving to do better. My passion for technology drives me to continuously learn and improve.

Want to receive update about our upcoming podcast?

Thanks for joining our newsletter.
Oops! Something went wrong.