熔断器hystrix

Posted by Run-dream Blog on August 11, 2021

背景

过载

在分布式系统中,某个服务可能因为流量过多,导致请求处理不过来,最后服务崩溃,甚至引起依赖它的服务也一直等不到返回,导致连锁反应一起崩溃,这种情况被称为雪崩。

限流

很容易就想到需要对单个服务进行限流,常见的限流方式有

  • 令牌桶算法 / 漏桶算法

    优点是实现简单,缺点是难以设置合理的限流阈值。

  • 自适应限流

    根据服务器的状态来进行流量控制。

熔断

限流的策略又包括:

  • 降级

    提供有损服务。

  • 熔断

    快速拒绝请求。429.

Hystrix(豪猪)

Hystrix 是由 Netflex 开发的一款开源组件,提供了基础的熔断功能, 本身是用 java 写的。

hystrix-go 是基于hystrix 的 go 实现。

如何使用

import github.com/afex/hystrix-go/hystrix
// 1.配置config
configA := hystrix.CommandConfig{
   Timeout:                int(3 * time.Second),
   MaxConcurrentRequests:  10,
   SleepWindow:            5000,
   RequestVolumeThreshold: 10,
   ErrorPercentThreshold:  30,
}
// 2.配置command
hystrix.ConfigureCommand("visitBaidu", configA)
// 3.执行Do方法
var prodRes *models.ProdListResponse
err := hystrix.Do("visitBaidu", func() error {
  _, err := http.Get("https://www.baidu.com/")
  return err
}, func(e error) error {
	fmt.Printf("handle  error:%v\n", err)
  return nil
})

Do方法:

Do 函数需要三个参数,

  • 第一个参数 commmand 名称,你可以把每个名称当成一个独立当服务
  • 第二个参数是处理正常的逻辑,比如 http 调用服务,返回参数是 err。
  • 如果处理失败,那么就执行第三个参数逻辑, 我们称为保底操作。由于服务错误率过高导致熔断器开启,那么之后的请求也直接回调此函数

配置含义:

  • Timeout 执行 command 的超时时间。
  • MaxConcurrentRequests command 的最大并发量
  • SleepWindow 当熔断器被打开后,SleepWindow 的时间就是控制过多久后去尝试服务是否可用了。
  • RequestVolumeThreshold 一个统计窗口10秒内请求数量。达到这个请求数量后才去判断是否要开启熔断
  • ErrorPercentThreshold:错误百分比,请求数量大于等于RequestVolumeThreshold并且错误率到达这个百分比后就会启动熔断

实现原理

设计模式

  • 命令模式 command

    将所有请求外部系统(或者叫依赖服务)的逻辑封装到 hystrix.Go 和 hystrix.Do 方法里

  • 观察者模式

    • Hystrix 通过观察者模式对服务进行状态监听
    • 每个任务都包含有一个对应的 metricExchange,所有MetricCollector都由 Registry 来进行维护
    • 在任务的不同阶段会往Metrics中写入不同的信息,Metrics会对统计到的历史信息进行统计汇总,供熔断器以及Dashboard监控时使用

具体实现

隔离

用 executorPool 来隔离不同 command 对应的操作

统计

使用滑动窗口 rolling 来统计数据。

流量控制

令牌算法

代码

大致过了一遍,

├─hystrix
│  │  circuit.go                        // CircuitBreaker 熔断器
│  │  circuit_test.go					
│  │  doc.go
│  │  eventstream.go										// StreamHandler 将每个请求的 metrics 发给 http 仪表盘
│  │  eventstream_test.go
│  │  hystrix.go												// API入口,提供 Go, Do 方法给用户调用
│  │  hystrix_test.go
│  │  logger.go													// logger 日志的抽象, 默认未实现
│  │  metrics.go												// metricExchange 系统的执行情况
│  │  metrics_test.go
│  │  pool.go														// executorPool command 对应的执行池
│  │  pool_metrics.go										// poolMetrics 执行池的统计新信息
│  │  pool_test.go
│  │  settings.go												// Settings command 的配置
│  │  settings_test.go
│  │  
│  ├─metric_collector										// Registry MetricCollector 收集器
│  │      default_metric_collector.go
│  │      metric_collector.go
│  │      
│  └─rolling														// Number, Timing滑动窗口
│          rolling.go
│          rolling_test.go
│          rolling_timing.go
│          rolling_timing_test.go
│          
├─loadtest
│  │  README.md
│  │  
│  └─service
│          main.go
│          
├─plugins
│      datadog_collector.go
│      graphite_aggregator.go
│      statsd_collector.go
│      statsd_collector_test.go
│      
└─scripts
        vagrant.sh

主要看了一下 rolling 和 metric_collector。

具体分析可以参考大佬的文章: hystrix-go 源码分析,讲的非常详细。

参考资料

hystrix-go 源码分析

一文彻底读懂 hystrix-go 源码

服务容错与保护方案 — Hystrix