← 中文资源

简体中文版本

Gobreaker 熔断器三态转换

用 sony/gobreaker 包裹不稳依赖:连续失败达到阈值跳闸打开,冷却后进半开态放行试探请求,状态转换全程回调可观测。

阅读英文原文

示例说明

重试适合偶发抖动,熔断适合持续故障——下游真挂了还继续调用是白白浪费资源与延迟。gobreaker 的三态闭环:闭合态正常放行并统计连续失败,ReadyToTrip 判定跳闸后进入打开态直接拒绝一切调用,冷却结束转半开态放限量试探请求,成功则回闭合、失败则回打开。

技术要点

  • ReadyToTrip 自定义跳闸条件
  • Timeout 控制打开态冷却时长
  • OnStateChange 钩子接入告警

代码解读

newBreaker 配置连续五次失败跳闸、十秒计数窗口、五秒冷却与半开三请求试探;状态转换回调打印迁移轨迹。flakyService 按取模规律失败,十二次调用演示闭合到打开再到恢复的完整周期——跳闸后调用直接返回熔断错误,不再触达服务。

使用建议

阈值按下游容量与恢复周期调。半开试探量小防打瘫恢复中的服务。熔断打开期间走降级路径而不是裸抛错误。

代码示例

package main

import (
	"errors"
	"fmt"
	"time"

	"github.com/sony/gobreaker"
)

var errFlaky = errors.New("dependency flaky")

type flakyService struct{ failRate int }

func (s *flakyService) Call(n int) (string, error) {
	if n%s.failRate == 0 {
		return "", errFlaky
	}
	return "payload", nil
}

func newBreaker(name string) *gobreaker.CircuitBreaker {
	return gobreaker.NewCircuitBreaker(gobreaker.Settings{
		Name:        name,
		MaxRequests: 3,               // 半开态最多放行 3 个试探
		Interval:    10 * time.Second, // 闭合态计数窗口
		Timeout:     5 * time.Second,  // 打开态冷却时长
		ReadyToTrip: func(counts gobreaker.Counts) bool {
			return counts.ConsecutiveFailures >= 5
		},
		OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {
			fmt.Printf("breaker %s: %s -> %s\n", name, from, to)
		},
	})
}

func guardedCall(cb *gobreaker.CircuitBreaker, svc *flakyService, n int) (string, error) {
	out, err := cb.Execute(func() (interface{}, error) {
		return svc.Call(n)
	})
	if err != nil {
		return "", err
	}
	return out.(string), nil
}

func main() {
	cb := newBreaker("flaky-api")
	svc := &flakyService{failRate: 3}

	for i := 1; i <= 12; i++ {
		v, err := guardedCall(cb, svc, i)
		fmt.Printf("call %02d -> value=%q err=%v\n", i, v, err)
	}
}