golang高效实现断路器模式的插件库circuit的使用

Golang高效实现断路器模式的插件库circuit的使用

Circuit是一个高效的、功能完整的Golang断路器模式实现,类似于Netflix的Hystrix。下面我将介绍如何使用这个库。

Mascot

主要特性

  • 无需强制使用goroutines
  • 可恢复的panic()
  • 与context.Context集成
  • 全面的指标跟踪
  • 高效的实现和基准测试
  • 低/零内存分配成本
  • 支持Netflix Hystrix仪表板
  • 多种错误处理功能
  • 通过expvar公开电路健康和配置
  • SLO跟踪
  • 可自定义的状态转换逻辑
  • 实时配置更改
  • 丰富的测试和示例

基本使用示例

1. 创建简单电路

// 管理所有电路
h := circuit.Manager{}
// 创建具有唯一名称的电路
c := h.MustCreateCircuit("hello-world")
// 调用电路
errResult := c.Execute(context.Background(), func(ctx context.Context) error {
  return nil
}, nil)
fmt.Println("Result of execution:", errResult)
// Output: Result of execution: <nil>

2. 使用回退逻辑

// 可以不使用管理器创建电路
c := circuit.NewCircuitFromConfig("hello-world-fallback", circuit.Config{})
errResult := c.Execute(context.Background(), func(ctx context.Context) error {
    return errors.New("this will fail")
}, func(ctx context.Context, err error) error {
    fmt.Println("Circuit failed with error, but fallback returns nil")
    return nil
})
fmt.Println("Execution result:", errResult)
// Output: Circuit failed with error, but fallback returns nil
// Execution result: <nil>

3. 在goroutine中运行

h := circuit.Manager{}
c := h.MustCreateCircuit("untrusting-circuit", circuit.Config{
  Execution: circuit.ExecutionConfig{
    // 在几毫秒后超时
    Timeout: time.Millisecond * 30,
  },
})

errResult := c.Go(context.Background(), func(ctx context.Context) error {
  // 睡眠30秒,远超过我们的超时时间
  time.Sleep(time.Second * 30)
  return nil
}, nil)
fmt.Printf("err=%v", errResult)
// Output: err=context deadline exceeded

Hystrix配置示例

configuration := hystrix.Factory{
  // Hystrix打开逻辑是在错误百分比后打开电路
  ConfigureOpener: hystrix.ConfigureOpener{
    // 我们改为等待10个请求,而不是20个,然后检查关闭
    RequestVolumeThreshold: 10,
    // 默认值匹配hystrix的默认值
  },
  // Hystrix关闭逻辑是先睡眠然后检查
  ConfigureCloser: hystrix.ConfigureCloser{
    // 默认值匹配hystrix的默认值
  },
}
h := circuit.Manager{
  // 告诉管理器在创建新电路时使用此配置工厂
  DefaultCircuitProperties: []circuit.CommandPropertiesConstructor{configuration.Configure},
}
// 此电路将从示例继承配置
c := h.MustCreateCircuit("hystrix-circuit")
fmt.Println("This is a hystrix configured circuit", c.Name())
// Output: This is a hystrix configured circuit hystrix-circuit

启用仪表板指标

// metriceventstream使用滚动统计报告电路信息
sf := rolling.StatFactory{}
h := circuit.Manager{
  DefaultCircuitProperties: []circuit.CommandPropertiesConstructor{sf.CreateConfig},
}
es := metriceventstream.MetricEventStream{
  Manager: &h,
}
go func() {
  if err := es.Start(); err != nil {
    log.Fatal(err)
  }
}()
// ES是一个http.Handler,所以可以直接传递给你的mux
http.Handle("/hystrix.stream", &es)
// ...
if err := es.Close(); err != nil {
  log.Fatal(err)
}
// Output:

运行时配置更改

// 从默认值开始
configuration := hystrix.ConfigFactory{}
h := circuit.Manager{
  // 告诉管理器在创建新电路时使用此配置工厂
  DefaultCircuitProperties: []circuit.CommandPropertiesConstructor{configuration.Configure},
}
c := h.MustCreateCircuit("hystrix-circuit")
fmt.Println("The default sleep window", c.OpenToClose.(*hystrix.Closer).Config().SleepWindow)
// 此配置更新函数是线程安全的。我们可以在电路活动时在运行时修改此配置
c.OpenToClose.(*hystrix.Closer).SetConfigThreadSafe(hystrix.ConfigureCloser{
  SleepWindow: time.Second * 3,
})
fmt.Println("The new sleep window", c.OpenToClose.(*hystrix.Closer).Config().SleepWindow)
// Output:
// The default sleep window 5s
// The new sleep window 3s

不将用户错误计为故障

c := circuit.NewCircuitFromConfig("divider", circuit.Config{})
divideInCircuit := func(numerator, denominator int) (int, error) {
  var result int
  err := c.Run(context.Background(), func(ctx context.Context) error {
    if denominator == 0 {
      // 此错误类型不计为电路故障
      return &circuit.SimpleBadRequest{
        Err: errors.New("someone tried to divide by zero"),
      }
    }
    result = numerator / denominator
    return nil
  })
  return result, err
}
_, err := divideInCircuit(10, 0)
fmt.Println("Result of 10/0 is", err)
// Output: Result of 10/0 is someone tried to divide by zero

性能基准

这个实现在每种配置下都比go-hystrix更高效。与其他实现相比,在高并发运行时对大多数情况来说更快。

仅限高并发通过电路(常见情况)的基准:

BenchmarkCiruits/cep21-circuit/Minimal/passing/75-8      	20000000	        87.7 ns/op	       0 B/op	       0 allocs/op
BenchmarkCiruits/GoHystrix/DefaultConfig/passing/75-8    	  500000	      2498 ns/op	     990 B/op	      20 allocs/op
BenchmarkCiruits/rubyist/Threshold-10/passing/75-8       	 2000000	       849 ns/op	     309 B/op	       4 allocs/op
BenchmarkCiruits/gobreaker/Default/passing/75-8          	 2000000	       698 ns/op	       0 B/op	       0 allocs/op
BenchmarkCiruits/handy/Default/passing/75-8              	 1000000	      1795 ns/op	       0 B/op	       0 allocs/op
BenchmarkCiruits/iand_circuit/Default/passing/75-8       	 5000000	       349 ns/op	       0 B/op	       0 allocs/op

开发

确保你的测试通过go test并且你的lint通过golangci-lint run

示例

你可以在/example目录中运行一组示例电路

go run example/main.go

输出看起来像这样:

go run example/main.go
2017/12/19 15:24:42 Serving on socket :8123
2017/12/19 15:24:42 To view the stream, execute:
2017/12/19 15:24:42   curl http://localhost:8123/hystrix.stream
2017/12/19 15:24:42
2017/12/19 15:24:42 To view expvar metrics, visit expvar in your browser
2017/12/19 15:24:42   http://localhost:8123/debug/vars
2017/12/19 15:24:42
2017/12/19 15:24:42 To view a dashboard, follow the instructions at https://github.com/Netflix/Hystrix/wiki/Dashboard#run-via-gradle
2017/12/19 15:24:42   git clone git@github.com:Netflix/Hystrix.git
2017/12/19 15:24:42   cd Hystrix/hystrix-dashboard
2017/12/19 15:24:42   ../gradlew jettyRun
2017/12/19 15:24:42
2017/12/19 15:24:42 Then, add the stream http://localhost:8123/hystrix.stream

如果你加载Hystrix仪表板(按照上面的说明),你应该能看到所有示例电路的指标。

dashboard


更多关于golang高效实现断路器模式的插件库circuit的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高效实现断路器模式的插件库circuit的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang断路器模式实现:circuit库使用指南

断路器模式(Circuit Breaker)是分布式系统中提高稳定性的重要模式,circuit是一个高效的Golang断路器实现库。下面我将详细介绍其使用方法。

circuit库简介

circuit是一个轻量级但功能强大的断路器实现,具有以下特点:

  • 基于时间窗口的错误率统计
  • 可配置的阈值和超时
  • 支持半开状态
  • 低开销

安装

go get github.com/cep21/circuit

基本使用

1. 创建断路器

import (
	"github.com/cep21/circuit"
	"github.com/cep21/circuit/closers/hystrix"
	"time"
)

func createCircuit() *circuit.Circuit {
	hystrixConfig := hystrix.Factory{
		// 当错误率超过50%时触发断路器
		ErrorPercentThreshold: 50,
		// 10秒统计窗口
		WindowLength:          time.Second * 10,
		// 最小请求数阈值,10秒内至少5个请求才统计
		NumBuckets:            10,
		// 断路器开启后5秒后进入半开状态
		SleepWindow:           time.Second * 5,
		// 半开状态最大允许请求数
		MaxConcurrentRequests: 100,
	}
	
	return circuit.NewCircuitFromConfig("my-service", circuit.Config{
		General: circuit.GeneralConfig{
			OpenToClosedFactory: hystrixConfig.ConfigureOpener,
			ClosedToOpenFactory: hystrixConfig.ConfigureCloser,
		},
	})
}

2. 使用断路器保护函数调用

func protectedCall(c *circuit.Circuit) (interface{}, error) {
	return c.Execute(context.Background(), func(ctx context.Context) (interface{}, error) {
		// 这里是被保护的业务逻辑
		resp, err := http.Get("http://example.com/api")
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		
		// 处理响应...
		return resp, nil
	}, nil)
}

高级功能

自定义失败判断

c.Execute(context.Background(), func(ctx context.Context) (interface{}, error) {
	resp, err := http.Get("http://example.com/api")
	if err != nil {
		return nil, circuit.SimpleBadRequest{Err: err}
	}
	
	if resp.StatusCode >= 500 {
		// 服务端错误计入断路器统计
		return nil, circuit.SimpleBadRequest{Err: fmt.Errorf("server error: %d", resp.StatusCode)}
	}
	
	// 4xx错误不计入断路器统计
	if resp.StatusCode >= 400 && resp.StatusCode < 500 {
		return nil, nil
	}
	
	return resp, nil
}, nil)

配置熔断器行为

circuit.NewCircuitFromConfig("custom-circuit", circuit.Config{
	Execution: circuit.ExecutionConfig{
		// 超时设置
		Timeout: time.Second * 2,
		// 最大并发请求数
		MaxConcurrentRequests: 100,
	},
	Fallback: circuit.FallbackConfig{
		// 熔断时执行的降级函数
		Config: circuit.FallbackFunc(func(ctx context.Context, err error) error {
			log.Printf("Fallback triggered due to: %v", err)
			return nil
		}),
	},
})

监控和指标

// 注册指标收集器
c := circuit.NewCircuitFromConfig("monitored-circuit", circuit.Config{
	Metrics: circuit.MetricsCollectors{
		// Prometheus指标
		&circuit.PrometheusCollector{},
		// 自定义日志收集
		circuit.MetricsCollectorFunc(func(r circuit.Metrics) {
			log.Printf("Circuit state: %s, requests: %d, errors: %d", 
				r.State, r.Requests, r.Errors)
		}),
	},
})

最佳实践

  1. 合理设置阈值:根据服务特点调整错误率和最小请求数阈值
  2. 区分错误类型:客户端错误(4xx)通常不应触发熔断
  3. 设置适当的超时:避免过长的超时导致资源耗尽
  4. 实现降级逻辑:熔断时返回缓存数据或默认值
  5. 监控熔断器状态:及时发现和解决系统问题

完整示例

package main

import (
	"context"
	"fmt"
	"github.com/cep21/circuit"
	"github.com/cep21/circuit/closers/hystrix"
	"log"
	"net/http"
	"time"
)

func main() {
	// 创建断路器
	circuitBreaker := createCircuit()
	
	// 模拟连续调用
	for i := 0; i < 20; i++ {
		res, err := protectedCall(circuitBreaker)
		if err != nil {
			log.Printf("Call failed: %v", err)
			continue
		}
		
		if res != nil {
			log.Printf("Call succeeded: %v", res)
		}
		
		time.Sleep(500 * time.Millisecond)
	}
}

func createCircuit() *circuit.Circuit {
	hystrixConfig := hystrix.Factory{
		ErrorPercentThreshold: 50,
		WindowLength:          time.Second * 10,
		NumBuckets:            10,
		SleepWindow:           time.Second * 3,
		MaxConcurrentRequests: 100,
	}
	
	return circuit.NewCircuitFromConfig("example-api", circuit.Config{
		General: circuit.GeneralConfig{
			OpenToClosedFactory: hystrixConfig.ConfigureOpener,
			ClosedToOpenFactory: hystrixConfig.ConfigureCloser,
		},
		Execution: circuit.ExecutionConfig{
			Timeout: time.Second * 1,
		},
		Fallback: circuit.FallbackConfig{
			Config: circuit.FallbackFunc(func(ctx context.Context, err error) error {
				log.Printf("Using fallback due to: %v", err)
				return nil
			}),
		},
	})
}

func protectedCall(c *circuit.Circuit) (interface{}, error) {
	return c.Execute(context.Background(), func(ctx context.Context) (interface{}, error) {
		// 模拟50%的失败率
		if time.Now().UnixNano()%2 == 0 {
			return nil, fmt.Errorf("simulated error")
		}
		return "success", nil
	}, nil)
}

这个示例展示了circuit库的核心功能,你可以根据实际需求调整配置参数。断路器模式能有效防止故障扩散,是构建弹性分布式系统的重要组件。

回到顶部