背景
由于微服务间通过RPC来进行数据交换,所以我们可以做一个假设:在IO型服务中,假设服务A依赖服务B和服务C,而B服务和C服务有可能继续依赖其他的服务,继续下去会使得调用链路过长,技术上称1->N扇出
问题
如果在A的链路上某个或几个被调用的子服务不可用或延迟较高,则会导致调用A服务的请求被堵住,堵住的请求会消耗占用掉系统的线程、io等资源,当该类请求越来越多,占用的计算机资源越来越多的时候,会导致系统瓶颈出现,造成其他的请求同样不可用,最终导致业务系统崩溃
- 服务器失败影响服务质量
- 超负荷导致整个服务失败
- 服务失败造成的雪崩效应
熔断
熔断模式:这种模式主要是参考电路熔断,如果一条线路电压过高,保险丝会熔断,防止火灾。放到我们的系统中,如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。
定义里面有几个量化的地方
- 目标服务调用慢或者超时:开启熔断的阀值量化
可以通过两个维度:时间与请求数
时间 多长时间内的超时请求达到多少,触发熔断
请求数 从服务启动,超时请求数达到多少,触发
这两个维度都需要记录超时请求数和统计总请求数
- 情况好转,恢复调用
如何量化情况好转:多长时间之后超时请求数低于多少关闭熔断
熔断状态
三种状态的切换
开 -- 半开 -- 关
开:使用快速失败返回,调用链结束
半开:当熔断开启一段时间后,尝试阶段
关:调用正常
实现机制
可以使用一段伪代码表示:
//正常request if( request is open) { //fastfail } else if( request is halfopen) { if ( request success count > recoverySampleVolume) { //state --> close } } //失败request if( request is failcount > requestVolumeThreshold && errorPercentage > threshold) { //close --> open }
请求熔断开启时,直接快速失败
是halfopen状态,如果成功处理次数是否大于恢复配置,就关闭熔断
如果失败次数超过阀值,开启熔断
而对于open-->halfopen的转换,可以通过定时器主动触发
具体实现
现在有很多开源的
failsafe:https://github.com/jhalterman/failsafe
Hystrix
个案实现
在没有熔断时,请求链路:
client --> request --> balance -- > handler
一个请求过来,通过负载均衡找到具体的server,再执行
加入熔断后:
client --> request --> circuitBreakerfilter --> balance -- > handler
CircuitBreakerFilter过滤掉被熔断的server,在负载均衡时,不再被选中
- getAllServers() 获取所有服务器列表
- 根据requestService,requestMethod获取熔断的servers
- 从allserverList中剔除这些server
熔断服务列表怎么维护呢?
正常状态 --> 熔断状态
1. 收到失败请求(e.g.超时,系统异常) 2. 判断此service是否配置了熔断策略 map<serviceName,circuitBreakerpolicy> - 根据serviceName,method,serverInfo获取CircuitBreakerCounter - counter对失败次数+1 - 此server是否在half open状态 HalfOpenServersMap<serverName+method,serverList> - 在:如果失败次数超过RecoverySampleVolume,openserversmap<servername+method,serverlist>进行put操作、并从HalfOpenServersMap中remove - 不在:请求数大于等于10笔(requestVolumeThreshold),且错误率达到60%(errorPercentage),openserversmap<servername+method,serverlist>进行put操作
熔断状态 --> 正常状态
1. 收到请求 2. 判断此service是否配置了熔断策略 map<serviceName,circuitBreakerpolicy> - 根据serviceName,method,serverInfo获取CircuitBreakerCounter - counter调用次数+1 - 若half-open 状态下的服务instance被调用次数超过取样的sample数,从HalfOpenServersMap中remove
疑问
- 错误率怎么计算?
- counter的实现
- 上面是close与open的转换,怎么转换到halfopen?
错误率= 错误次数/请求次数
halfopen状态
在上面的提到,被熔断的服务,如果情况好转就会关闭熔断!“情况好转”:什么时候去判断情况好转,怎么判断情况好转两方面
- 在加入到openserversmap时,同时开启延迟时间窗口后的定时任务
- 从openserversmap中移除,加入到halfOpenServersMap
counter实现
- 简单点:AtomicLong,如当是halfopen时,使用这种简单的计数器叠加
- 滑动时间窗口实现
VS 降级
提到熔断,不得不起一下降级。两者的区别
有时语言真是乏力,不容易表达清楚,罗列一下
熔断是框架提供,不管业务什么样,防止系统雪崩,都需要提供一下基本功能;而降级与业务有关,自动或手动。比如支付,有很多种支付方式,储蓄卡,信用卡,支付宝,微信。若发现某一支付通道不稳定,或压力过大,手动先关闭,这就是一种降级
由此可看出:
- 触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
- 管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
- 实现方式不一样
参考
微服务熔断与隔离
CircuitBreaker