In modern microservices architectures, services often depend on one another. But what happens when one service fails or becomes slow? It can trigger a cascade of failures, bringing down your entire system. To prevent this, we can implement the Circuit Breaker design pattern, which is essential for building resilient, fault-tolerant applications.
At Bitscorp, we use this pattern to protect our services, and gobreaker is our preferred library for implementing it in Go.
What is the Circuit Breaker Pattern?
The Circuit Breaker pattern is modeled after a real-world electrical circuit breaker. It wraps a protected function call (like a network request) in a state machine that monitors for failures. It has three states:
-
Closed: The initial state. Requests are allowed to pass through to the protected service. The circuit breaker counts the number of failures, and if they exceed a certain threshold within a time window, it "trips" and moves to the Open state.
-
Open: For a configured timeout period, all requests to the protected service fail immediately without even attempting to execute. This prevents the application from repeatedly calling a service that is likely down. After the timeout, the circuit breaker moves to the Half-Open state.
-
Half-Open: In this state, a limited number of test requests are allowed to pass through. If these requests succeed, the circuit breaker resets and returns to the Closed state. If any request fails, it trips back to the Open state to begin the timeout period again.
This pattern prevents cascading failures and allows a failing service time to recover.
Implementing a Circuit Breaker in Go with gobreaker
gobreaker is a simple and effective Go library for implementing the circuit breaker pattern. Let's walk through an example of wrapping a potentially failing payment request.
The Scenario: A Flaky Payment Gateway
Imagine we have a payment service that communicates with an external gateway. This gateway is sometimes unavailable, causing our requests to time out or fail. We want to prevent our application from getting stuck waiting for a response and avoid bombarding the gateway when it's already struggling.
Installation
First, add gobreaker to your project:
go get github.com/sony/gobreaker
Example Implementation
Here’s a complete example demonstrating how to use gobreaker to protect a payment request function.
package mainimport ("errors""fmt""log""math/rand""time""github.com/sony/gobreaker")// simulatePaymentGateway simulates making a request to an external payment gateway.// To demonstrate the circuit breaker, it will fail randomly.func simulatePaymentGateway(amount float64) (string, error) {// Fail 60% of the time to simulate an unreliable service.if rand.Intn(100) < 60 {return "", errors.New("payment gateway is unavailable")}return fmt.Sprintf("payment of $%.2f successful", amount), nil}func main() {// Configure the circuit breaker settings.settings := gobreaker.Settings{Name: "PaymentGateway",MaxRequests: 3, // Number of requests allowed in Half-Open state.Interval: 5 * time.Second, // The cyclical period of the 'Closed' state.Timeout: 10 * time.Second, // The period of the 'Open' state.// This function is called when the state changes.OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {log.Printf("Circuit Breaker '%s' changed state from %s to %s\n", name, from, to)},}// Create a new CircuitBreaker with the defined settings.cb := gobreaker.NewCircuitBreaker(settings)// Simulate making multiple payment requests.for i := 1; i <= 20; i++ {paymentAmount := 50.00// The Execute function wraps our protected call.body, err := cb.Execute(func() (interface{}, error) {return simulatePaymentGateway(paymentAmount)})if err != nil {log.Printf("Request #%d: FAILED. Error: %v (Circuit Breaker State: %s)", i, err, cb.State())} else {log.Printf("Request #%d: SUCCESS. Response: %s (Circuit Breaker State: %s)", i, body, cb.State())}// Wait a bit between requests.time.Sleep(500 * time.Millisecond)}}
How It Works
-
Configuration: We create a
gobreaker.Settingsstruct.MaxRequests: When the circuit isHalf-Open, it allows 3 requests to pass through to test if the service has recovered.Interval: In theClosedstate, the failure counts are reset after this interval (5 seconds).Timeout: When the circuit isOpen, it will stay open for 10 seconds before transitioning toHalf-Open.OnStateChange: A useful callback to log state transitions, which is great for monitoring.
-
Execution: We wrap our
simulatePaymentGatewaycall insidecb.Execute().gobreakermonitors the return value of this function. If it returns an error, it counts it as a failure.- When the circuit is Closed,
Executecalls our function. After enough failures, it opens the circuit. - When the circuit is Open,
Executeimmediately returnsgobreaker.ErrOpenStatewithout running our function. - After the
Timeout, the state becomes Half-Open, andExecutelets a few requests (MaxRequests) through. Successes will close the circuit, while any failure will re-open it.
Example Output
Running the code above might produce output like this:
2025/11/15 10:30:01 Request #1: FAILED. Error: payment gateway is unavailable (Circuit Breaker State: closed)
2025/11/15 10:30:01 Request #2: SUCCESS. Response: payment of $50.00 successful (Circuit Breaker State: closed)
2025/11/15 10:30:02 Request #3: FAILED. Error: payment gateway is unavailable (Circuit Breaker State: closed)
2025/11/15 10:30:03 Request #4: FAILED. Error: payment gateway is unavailable (Circuit Breaker State: closed)
2025/11/15 10:30:03 Request #5: FAILED. Error: payment gateway is unavailable (Circuit Breaker State: closed)
2025/11/15 10:30:04 Request #6: FAILED. Error: payment gateway is unavailable (Circuit Breaker State: closed)
2025/11/15 10:30:04 Circuit Breaker 'PaymentGateway' changed state from closed to open
2025/11/15 10:30:04 Request #7: FAILED. Error: circuit breaker is open (Circuit Breaker State: open)
2025/11/15 10:30:05 Request #8: FAILED. Error: circuit breaker is open (Circuit Breaker State: open)
... (fails immediately for 10 seconds) ...
2025/11/15 10:30:14 Circuit Breaker 'PaymentGateway' changed state from open to half-open
2025/11/15 10:30:14 Request #15: FAILED. Error: payment gateway is unavailable (Circuit Breaker State: half-open)
2025/11/15 10:30:14 Circuit Breaker 'PaymentGateway' changed state from half-open to open
2025/11/15 10:30:15 Request #16: FAILED. Error: circuit breaker is open (Circuit Breaker State: open)
As you can see, once the gateway is deemed unhealthy, the circuit breaker opens and starts failing requests fast, protecting both our application and the downstream service.
Why This Matters
By implementing the circuit breaker pattern, you achieve:
- Resilience: Your application can gracefully handle failures in downstream services without crashing.
- Fault Tolerance: You prevent a single point of failure from causing a system-wide outage.
- Better User Experience: Users get a faster response (an error message) instead of a long wait, and the system recovers automatically.
For any distributed system, especially in a microservices environment, the circuit breaker pattern is not just a "nice-to-have"—it's a necessity for building robust, production-grade services.
Need help building resilient, scalable backend systems in Go? Contact us today to discuss how we can help you implement best-practice design patterns for your next project.