# How to Use and Configure the Circuit Breaker Middleware in Kratos

> Learn how Kratos circuit breaker middleware protects services from cascading failures. Discover configuration options WithGroup and WithCircuitBreaker for optimal performance.

- Repository: [Kratos/kratos](https://github.com/go-kratos/kratos)
- Tags: how-to-guide
- Published: 2026-03-02

---

**The circuit breaker middleware in Kratos protects services from cascading failures by intercepting client requests through a per-operation SRE-style breaker that automatically rejects traffic when error thresholds are exceeded, configurable via `WithGroup()` and `WithCircuitBreaker()` options.**

Kratos provides a client-side circuit breaker middleware located in `middleware/circuitbreaker` that prevents cascading failures by short-circuiting calls to unhealthy resources. Built on top of the aegis library, this middleware automatically isolates failing operations and returns structured errors without hitting the downstream service.

## How the Circuit Breaker Middleware Functions in Kratos

### Per-Operation Breaker Initialization

In [`middleware/circuitbreaker/circuitbreaker.go`](https://github.com/go-kratos/kratos/blob/main/middleware/circuitbreaker/circuitbreaker.go), the middleware constructs a **group** (`group.Group`) that lazily instantiates a breaker for each distinct operation using `info.Operation()`. The default generator returns an **SRE-style breaker** (`sre.NewBreaker()`) with sensible defaults: failure-rate threshold of 0.5, request-volume threshold of 20, and a 5-second sleep window.

This design ensures that each RPC method or HTTP endpoint receives an independent circuit breaker, preventing a failure in one operation from affecting unrelated traffic.

### Request Flow and Allow Checks

When processing requests, the middleware extracts client transport info via `transport.FromClientContext` and retrieves the operation-specific breaker using `opt.group.Get(info.Operation())`. The `breaker.Allow()` method determines if the request proceeds.

If the breaker rejects the call, the middleware increments the failure counter via `breaker.MarkFailed()` and returns **`ErrNotAllowed`** with HTTP status 503 and error code "CIRCUITBREAKER". This rejection occurs immediately without invoking the downstream handler.

### Error Classification and Outcome Recording

For permitted requests, the middleware invokes the downstream handler and records outcomes based on error type. **Server-side errors** (`InternalServer`, `ServiceUnavailable`, `GatewayTimeout`) trigger `breaker.MarkFailed()`, while all other outcomes including success trigger `breaker.MarkSuccess()`. This distinction ensures that client-side errors (4xx) do not artificially open the circuit against healthy downstream services.

## Configuring the Circuit Breaker Middleware in Kratos

### Using Default SRE Breakers

Without configuration, `cb.Client()` creates a fresh group generating SRE breakers per operation. This zero-config approach provides immediate protection with industry-standard defaults suitable for most microservices.

```go
import (
    cb "github.com/go-kratos/kratos/v2/middleware/circuitbreaker"
    "github.com/go-kratos/kratos/v2/transport/http"
)

func NewClient() *http.Client {
    return http.NewClient(
        http.WithMiddleware(cb.Client()),
    )
}

```

### Customizing Breaker Thresholds with WithCircuitBreaker

Use **`WithCircuitBreaker(gen func() circuitbreaker.CircuitBreaker)`** to supply a custom factory function. This enables tuning thresholds, timeout windows, or swapping the SRE implementation for another algorithm entirely.

```go
import (
    cb "github.com/go-kratos/kratos/v2/middleware/circuitbreaker"
    "github.com/go-kratos/aegis/circuitbreaker"
    "github.com/go-kratos/aegis/circuitbreaker/sre"
    "time"
)

func customBreakerFactory() circuitbreaker.CircuitBreaker {
    return sre.NewBreaker(
        sre.WithFailureRateThreshold(0.30),
        sre.WithSleepWindow(10 * time.Second),
    )
}

func NewClientWithCustomCB() *http.Client {
    return http.NewClient(
        http.WithMiddleware(
            cb.Client(cb.WithCircuitBreaker(customBreakerFactory)),
        ),
    )
}

```

### Sharing Breaker Groups Across Clients with WithGroup

**`WithGroup(g *group.Group[circuitbreaker.CircuitBreaker])`** replaces the internal group with a user-provided instance, useful for sharing circuit state across multiple client instances or pre-populating method-specific configurations. Passing `nil` disables the group and will cause a panic if used.

```go
import (
    "github.com/go-kratos/kratos/v2/internal/group"
    cb "github.com/go-kratos/kratos/v2/middleware/circuitbreaker"
    "github.com/go-kratos/aegis/circuitbreaker"
    "github.com/go-kratos/aegis/circuitbreaker/sre"
)

var sharedGroup = group.NewGroup(func() circuitbreaker.CircuitBreaker {
    return sre.NewBreaker()
})

func NewClientUsingGroup() *http.Client {
    return http.NewClient(
        http.WithMiddleware(
            cb.Client(cb.WithGroup(sharedGroup)),
        ),
    )
}

```

## Summary

- The circuit breaker middleware in Kratos uses a per-operation group pattern in [`middleware/circuitbreaker/circuitbreaker.go`](https://github.com/go-kratos/kratos/blob/main/middleware/circuitbreaker/circuitbreaker.go) to isolate failures between different RPC methods.
- Default **SRE breakers** provide immediate protection with 50% failure-rate thresholds, 20-request volume minimums, and 5-second sleep windows.
- **WithCircuitBreaker()** enables custom breaker factories for fine-tuned reliability policies per service.
- **WithGroup()** allows sharing breaker state across multiple client instances for consistent circuit state and global metrics.
- Server-side errors (5xx) automatically mark failures via `MarkFailed()`, while client errors (4xx) and successes mark healthy states via `MarkSuccess()`.

## Frequently Asked Questions

### What error does the circuit breaker return when rejecting requests?

When the breaker is open, the middleware returns `ErrNotAllowed` with HTTP status 503 and error code "CIRCUITBREAKER". This happens immediately after `breaker.Allow()` rejects the request in [`middleware/circuitbreaker/circuitbreaker.go`](https://github.com/go-kratos/kratos/blob/main/middleware/circuitbreaker/circuitbreaker.go), preventing the call from reaching the downstream service and protecting the client from waiting on a failing resource.

### How does Kratos determine which requests count as failures?

The middleware inspects returned errors in [`middleware/circuitbreaker/circuitbreaker.go`](https://github.com/go-kratos/kratos/blob/main/middleware/circuitbreaker/circuitbreaker.go) and treats **server-side errors** (`InternalServer`, `ServiceUnavailable`, `GatewayTimeout`) as failures by calling `breaker.MarkFailed()`. All other outcomes, including success and client-side errors (4xx), trigger `breaker.MarkSuccess()`, ensuring that client mistakes don't artificially open the circuit against healthy downstream services.

### Can I use different breaker configurations for different API endpoints?

Yes. Because the middleware uses a `group.Group` that keys breakers by `info.Operation()`, each RPC method or HTTP endpoint automatically receives an independent breaker instance with its own failure counters. You can further customize this by providing a custom group via `WithGroup()` that returns different breaker configurations based on the operation name, or by using a custom factory function that inspects context metadata.

### What happens if I pass nil to WithGroup?

Passing `nil` to `WithGroup()` disables the group mechanism entirely. If the middleware attempts to retrieve a breaker from a nil group via `opt.group.Get()`, it will panic. Always provide a valid `*group.Group[circuitbreaker.CircuitBreaker]` instance when using this configuration option.