ling-baseling-base

熔断器

Closed/Open/Half-Open 熔断状态机

在线 Playground

在浏览器中直接体验本页相关 API,无需本地安装 Go 环境。

熔断器

线程安全熔断器,Closed → Open → Half-Open 状态转换。

go get github.com/LingByte/ling-base/common/circuitbreaker

以下为 common/circuitbreaker 包 README 全文。

A thread-safe circuit breaker with Closed / Open / Half-Open state machine, sliding-window failure-rate statistics, and configurable thresholds.

States

       failure rate >= threshold          recovery timeout
  ┌──────────────────────────┐      ┌─────────────────────┐
  │                          ▼      │                     ▼
CLOSED ◄─────────────────── HALF-OPEN ◄───────────────── OPEN
  ▲   all trial requests succeed    │   any trial request fails
  └─────────────────────────────────┘
  • Closed: requests pass through. Failures are tracked in a sliding window. When the failure rate exceeds the threshold, the breaker trips to Open.
  • Open: requests are rejected immediately with ErrCircuitOpen. After the recovery timeout, the breaker transitions to Half-Open.
  • Half-Open: a limited number of trial requests are allowed. If all succeed, the breaker closes. If any fails, it re-opens.

Quick start

import "github.com/LingByte/ling-base/common/circuitbreaker"

cb := circuitbreaker.New(circuitbreaker.Config{
    MaxRequests:      5,      // trial requests in Half-Open
    FailureThreshold: 0.5,    // 50% failure rate trips the breaker
    MinRequests:      10,     // need at least 10 requests before evaluating
    RecoveryTimeout:  30 * time.Second,
    Name:             "my-service",
    OnStateChange: func(name string, from, to circuitbreaker.State) {
        log.Printf("breaker %s: %s%s", name, from, to)
    },
})

err := cb.Execute(ctx, func(ctx context.Context) error {
    return callRemoteService(ctx)
})
if errors.Is(err, circuitbreaker.ErrCircuitOpen) {
    // fallback / return cached response
}

Integration with retry

Combine circuit breaker with retry for resilient remote calls — retry handles transient failures within a single call, while the circuit breaker prevents cascading failures across calls:

err := cb.Execute(ctx, func(ctx context.Context) error {
    return retry.Do(ctx, func(ctx context.Context) error {
        return callRemote(ctx)
    },
        retry.WithMaxAttempts(3),
        retry.WithExponentialBackoff(100*time.Millisecond, 5*time.Second, 2.0, true),
    )
})

Or use the retry.WithCircuitBreaker option:

err := retry.Do(ctx, op,
    retry.WithMaxAttempts(3),
    retry.WithCircuitBreaker(cb),
)

When the breaker is open, the retry loop stops immediately with ErrCircuitOpen — no further attempts are made.

Configuration

FieldDescriptionDefault
MaxRequestsTrial requests allowed in Half-Open5
FailureThresholdFailure rate (0.0–1.0) that trips the breaker0.5
MinRequestsMinimum requests before evaluating failure rate10
RecoveryTimeoutHow long the breaker stays Open30s
SlidingWindowSizeNumber of recent outcomes in the window100
OnStateChangeCallback on state transitionsnil
NameBreaker identifier (for logs/metrics)""

Metrics

m := cb.Metrics()
// m.State, m.WindowLen, m.Failures, m.Successes, m.FailureRate, m.Generation

License

MIT

On this page