背景 打算用五篇的篇幅介绍一下限流、熔断、降级、隔离、超时重试。继《稳定性五件套-限流的原理和实现》,这是第二篇。主要说一说熔断。 熔断的概念 上篇提到过我对限流和熔断区别的理解。限流作用是防御上游流量超过处理能力的手段,熔断作用是容错下游的快速失败手段。 超时重试也是容错下游的手段。也经常有人认为熔断和超时重试是一回事,或者需要统一来看,因为都是可以基于熔断器来实现。但我认为它们有本质的区别,熔断针对的是下游的各种错误:超时是一种情况;错误率过高也是一种情况;还有某些特定场景需要对一种特定的返回码做熔断,比如服务拒绝请求。 总而言之,超时重试和熔断有两点主要的不同。 第一,超时重试和熔断要解决的问题不同。 熔断是解决服务级联故障的问题;超时重试是解决分布式系统的三态问题。分布式系统的三态是成功、失败和超时。对于超时,如果重试成功,最终是成功的。如果重试之后都失败,那就失败的。这样三态问题可以转换为两态。 第二,超时重试是针对单个请求的处理。而熔断是针对一类或者一组请求的处理。比如一般熔断要结合计数器,当错误超过阈值才熔断。 举个生活中的限流例子: 上篇提到小A经过一段时间终于找到了心仪的女朋友。但是实际上他心仪的女朋友并不只一个。而是有5个。为了显示对女朋友的喜爱和尊重,他女朋友们发过来的消息他决定基本采取秒回的方式。所以小A很忙。 小A同时开着5个消息窗口。实时去关注这些窗口的动态。如果他一些女朋友有段时间很爱聊天。人的精力是有限的,是吧。所以他就采用一定的限流策略。对于其中任意一个女朋友来说:如果1分钟内超过了3条消息需要回复,他就只回复其中的一条。 举个生活中的熔断例子: 小A除了要接收他女朋友们的消息之外,作为一个暖男。小A还会时不时自己主动嘘寒问暖一下。发消息给他女朋友们提醒她们多喝水。当然他女朋友们自己并不知道有其他人,只知道自己收到了消息。有的女朋友比如小C就回复很快,那小A就会和小C保持一个很频繁的来回通信。 有的女朋友比如小D是个程序媛,有时候需要开各种会。小A发给消息很长时间都不回,那他还是老是开着窗口不断看着小D有没有回消息就太浪费了。这时候小A会采用熔断措施,如果超过10分钟不回,他会先关掉窗口。隔半小时再打开看看。 小A还有个女朋友小E,她电脑中了病毒,给小A发的都是各种垃圾广告。小A判断这些信息没有必要看了,也做了熔断,关闭了窗口。但他还是会隔半小时再打开看看小E的电脑病毒问题有没有解决,是不是可以正常发消息了。 举个生活中的超时重试例子: 小A终于觉得自己女朋友实在是太多了,所以他决定和其中一个小F分手。但是小A想好的分手的台词发消息过去,小F都不回。小A都不知道到底分手是成功还是不成功。小A就很小心的每10分钟给小E发个消息问她是否同意这个决定。这就是超时重试。 熔断的原理 熔断本质上是做快速失败,防止级联故障引起雪崩。它的主要采用的手段是基于断路器的设计模式。 断路器有基本模式和扩展模式。 基本模式中,断路器由两个状态和一个动作组成:断路器打开状态、断路器关闭状态和跳闸动作。在断路器关闭状态下,请求过来每次都要先经由跳闸动作,由跳闸动作判断是否需要将状态改成断路器打开状态。断路器打开状态下,请求直接执行快速失败的动作,不会向后请求。 扩展模式中,断路器增加了一个半开状态,它允许有限数量的请求通过,如果执行成功,恢复到关闭状态;如果失败,则恢复到开放,然后重启定时器,一段时间后再用半开状态来尝试。扩展模式可以半自动化的进行熔断恢复,避免了基本模式中,一旦断路器被打开完全依赖人工判断是否恢复的弊端。 扩展模式对应的状态机流转如下: 熔断的实现 基础实现 在Java中业界用的比较多的是hystrix或者resilience4j来实现熔断。原理差不多。实际上resilience4j是受到hystrix启发而形成的。 下面以hystrix为例进行讲解。这是一个根据token认证的服务,正常情况下调用authenticate方法会调用下游来判断token是否有效。但是如果下游一旦出现问题了,因为这是一个内网服务,平时很少有非法流量进来,并且内网服务安全性可控。这个认证的统计学意义更大一些。所以在下游出现故障时可以快速降级直接返回认证成功。避免对整个服务造成影响。 从上面代码可知,使用方面关注点在于@HystrixCommand这个注解。基本用法就是上面的代码示例。 下面简单讲解一下源码。重点是HystrixCircuitBreakerImpl这个实现类 public static class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker { 1>这里面成员变量中HystrixCommandProperties是用来处理@HystrixCommand这个注解的commandProperties的。 和断路器相关的主要是下面这些: 1>如果circuitBreakerForceOpen = true会将断路器强制打开,circuitBreakerForceClose = true会将断路器强制关闭。这个一般配合配置中心来用,起到人为干预的作用。 2>HystrixCommandMetrics是计数器,用这里面存的健康请求数量来判断是否需要熔断。 3>circuitOpen即为断路器状态,有打开和关闭两种。 4>markSuccess() 方法是在调用attemptExecution正常逻辑成功时,调用 #markSuccess() 方法,关闭断路器。这时候HystrixCommandMetrics也会重置。 5>allowRequest()是用来判断是否允许指令执行。 6>allowSingleTest()是用来判断是否打开熔断到一定时间了,到了的话就测试一下是否已经恢复了。 7>isOpen()是判断断路器是否打开。 高阶实现 如果项目中全套使用了spring cloud,可以使用feign。但是如果只是想使用熔断,还是建议直接使用hystrix。个人实验如果想直接使用spring cloud feign做熔断,坑比较多。 |
|