Open loadlj opened 4 years ago
微服务熔断(circuit breaker) 可以理解为是一个保护自身服务以及其调用服务的开关。假设我们服务需要调用一个外部服务B,当B不可用的时候如果我们没有这个熔断器,请求本身还会一直打到B,相应自身系统的latency也会增加,从而造成整体服务的不可用,产生雪崩效应。如果加入了熔断机制,这时候有一个可靠的fallback,牺牲系统的部分准确性可以换来整体的可用性提高。 一些更加详细的介绍可以参考。
Circuit breaker的实现有很多种,主流的解决方案大多都是基于Netflix的 Hystrix 来做的。 下面是Hystrix的概念图
HystrixCommand 和 HystrixObservableCommand:这是Java里面的函数,理解起来就是一个同步一个异步,对应, hystrix.Do 和 hystrix.Go 。
Circuit Open, Half Open 以及 close:一个正常的Hystrix 状态有三种,当服务正常运行的时候是close。当服务的错误率到达指定的时候就 Open。当Open之后过去一段时间,Hystrix 会让单独的request通过,测服务是否恢复,这个时候就叫做Half open。
Hystrix cache:hystrix 支持将命令的返回结果cache住,但是在Golang的Hystrix官方库并没有支持。
FallBack: 当circuit breaker Open之后,或者其他错误发生(queue或者Pool满了),这个时候就可以使用Fallback来兜底这些错误逻辑,fallback还是很有用的。
这里以Golang的版本来作为参考,实现起来应该是有几个要点需要注意的: 最大并发的控制(queue实现),同步实现(hystrix.Do),异步实现(Hystrix.Go)。 代码实现并不复杂,先看 Hystrix.Go 的实现,简化的代码如下
go func() { case cmd.ticket = <-circuit.executorPool.Tickets: ticketChecked = true ticketCond.Signal() cmd.Unlock() default: ticketChecked = true ticketCond.Signal() cmd.Unlock() returnOnce.Do(func() { returnTicket() cmd.errorWithFallback(ctx, ErrMaxConcurrency) reportAllEvent() }) return } runStart := time.Now() runErr := run(ctx) returnOnce.Do(func() { defer reportAllEvent() cmd.runDuration = time.Since(runStart) returnTicket() if runErr != nil { cmd.errorWithFallback(ctx, runErr) return } cmd.reportEvent("success") }) }() go func() { timer := time.NewTimer(getSettings(name).Timeout) defer timer.Stop() select { case <-cmd.finished: // returnOnce has been executed in another goroutine case <-ctx.Done(): returnOnce.Do(func() { returnTicket() cmd.errorWithFallback(ctx, ctx.Err()) reportAllEvent() }) return case <-timer.C: returnOnce.Do(func() { returnTicket() cmd.errorWithFallback(ctx, ErrTimeout) reportAllEvent() }) return } }()
先判断cb状态是否允许请求,然后再尝试从ticketPool里面去拿一个ticket执行对应的命令,执行完就放回ticket。另外一个goroutine用来控制前一个goroutine的生命周期(判断是否超时,ctx结束等)。
Hystrix.Do 的方法较为简单,只有一些简单的逻辑判断就不贴代码了。
Pool的实现很简练,就是一个buffered channel,完整逻辑如下.
package hystrix type executorPool struct { Name string Metrics *poolMetrics Max int Tickets chan *struct{} } func newExecutorPool(name string) *executorPool { p := &executorPool{} p.Name = name p.Metrics = newPoolMetrics(name) p.Max = getSettings(name).MaxConcurrentRequests p.Tickets = make(chan *struct{}, p.Max) for i := 0; i < p.Max; i++ { p.Tickets <- &struct{}{} } return p } func (p *executorPool) Return(ticket *struct{}) { if ticket == nil { return } p.Metrics.Updates <- poolMetricsUpdate{ activeCount: p.ActiveCount(), } p.Tickets <- ticket } func (p *executorPool) ActiveCount() int { return p.Max - len(p.Tickets) }
其他的像包括错误率统计以及allowSingleTest等逻辑,具体可以参考源代码,实现的都不复杂。
基于Hystrix 的circuit breaker功能还是十分强大的,但是需要注意的是每一个请求都会额外产生两个goroutine, 统计错误率等一系列的方法也是比较吃CPU的,当服务对于性能要求较高的时候酌情使用。
在使用cb的时候也需要考虑到 goroutine的生命周期,合理利用Ctx去释放掉相应的资源,不然是会很容易造成goroutine leakd的。
可以借鉴 踩踩
微服务熔断与隔离
什么是微服务熔断与隔离
微服务熔断(circuit breaker) 可以理解为是一个保护自身服务以及其调用服务的开关。假设我们服务需要调用一个外部服务B,当B不可用的时候如果我们没有这个熔断器,请求本身还会一直打到B,相应自身系统的latency也会增加,从而造成整体服务的不可用,产生雪崩效应。如果加入了熔断机制,这时候有一个可靠的fallback,牺牲系统的部分准确性可以换来整体的可用性提高。 一些更加详细的介绍可以参考。
Hystrix
Circuit breaker的实现有很多种,主流的解决方案大多都是基于Netflix的 Hystrix 来做的。 下面是Hystrix的概念图
一些概念
HystrixCommand 和 HystrixObservableCommand:这是Java里面的函数,理解起来就是一个同步一个异步,对应, hystrix.Do 和 hystrix.Go 。
Circuit Open, Half Open 以及 close:一个正常的Hystrix 状态有三种,当服务正常运行的时候是close。当服务的错误率到达指定的时候就 Open。当Open之后过去一段时间,Hystrix 会让单独的request通过,测服务是否恢复,这个时候就叫做Half open。
Hystrix cache:hystrix 支持将命令的返回结果cache住,但是在Golang的Hystrix官方库并没有支持。
FallBack: 当circuit breaker Open之后,或者其他错误发生(queue或者Pool满了),这个时候就可以使用Fallback来兜底这些错误逻辑,fallback还是很有用的。
实现原理
这里以Golang的版本来作为参考,实现起来应该是有几个要点需要注意的: 最大并发的控制(queue实现),同步实现(hystrix.Do),异步实现(Hystrix.Go)。 代码实现并不复杂,先看 Hystrix.Go 的实现,简化的代码如下
先判断cb状态是否允许请求,然后再尝试从ticketPool里面去拿一个ticket执行对应的命令,执行完就放回ticket。另外一个goroutine用来控制前一个goroutine的生命周期(判断是否超时,ctx结束等)。
Hystrix.Do 的方法较为简单,只有一些简单的逻辑判断就不贴代码了。
Pool的实现很简练,就是一个buffered channel,完整逻辑如下.
其他的像包括错误率统计以及allowSingleTest等逻辑,具体可以参考源代码,实现的都不复杂。
小结
基于Hystrix 的circuit breaker功能还是十分强大的,但是需要注意的是每一个请求都会额外产生两个goroutine, 统计错误率等一系列的方法也是比较吃CPU的,当服务对于性能要求较高的时候酌情使用。
在使用cb的时候也需要考虑到 goroutine的生命周期,合理利用Ctx去释放掉相应的资源,不然是会很容易造成goroutine leakd的。