1.背景
1.1 为什么要做熔断
加入一个系统qps达到1000,如果a调用b服务,b挂掉了,可能瞬间把a机器线程池打满,b调用c就会造成雪崩效应,熔断是系统的一种保护机制
1.2 现在哪些熔断措施
hystrix熔断,状态机熔断机制,好处是可以自动熔断和恢复,细粒度把控熔断,坏处是在集群负载均衡很好
的情况下,单机平摊流量才能比较好的实现熔断效果。
redis计数法分布式熔断机制,利用redis的incr和失效时间判断在一段时间内的异常请求数量达到一定值,直接熔断,可以通过状态位设置判断是否熔断,熔断置状态位为熔断。可以通过diamond在分布式环境下保证
状态位
1.3 怎样选择和是的熔断策略
根据业务场景选择熔断策略,可以redis和hystrix搭配使用
2.实际业务场景
2.1 问题描述
收银台业务,用户支付需要拉取收银台,收银台会依赖第三方服务拉取支付方式(支付通道)展示给用户,
有余额支付,这时候会去取用户余额,会调卡券平台的b服务,在快手接入进来的时候,b服务突入挂了,快手的支付量级直接给系统带来1000qps的流量,直接造成dubbo线程池打满,整个支付系统雪崩,用户收银台渲染大量失败。
2.2 解决思路
a.由于卡券超时时间之前设置的5000ms,其实一般调用都是30ms,就是一个查询服务,可以设置超时时间为1000ms,之前代码是
try{
a->b()
}catch(xxxException e){
log.warn(e);
}
这时候如果外部没有限流,没有熔断很容易把机器打满,造成雪崩,可以考虑
使用redis限流,redis计数熔断,diamond配置熔断标志位,本地设置一段时间重试,待b服务恢复的时候自动恢复调用,同时区分异常,如果是dubbo线程池满的错误,进异常任务队列,设置每个失败的时间,1s内的在恢复后重试,超过1s的直接返回前端系统繁忙,提醒用户5分钟之后重试支付,具体为代码如下:
if(diamond.isCut == false){
a->b;
} catch(TimeOutException e}
{
if(redis.get(a.name(),1s)<100)
{
log.warn(e);
redis.incr(a.name(),1s);
} else{
cutTime = System.CurrentTime();
diamond.setIsCut(true);
} catch(rejectException e){
if(redis.get(a.name(),1s)<100)
{
log.warn(e);
redis.incr(a.name(),1s);
} else{
cutTime = System.CurrentTime();
diamond.setIsCut(true);
errorList.add(order,errorOrderTime);
}
}
if(diamond.isCut == true && System.CurrentTime - cutTime == 2s)
{
try{
result = a->b()
}catch(Exception e){
log.warn("还没有恢复");
cutTime = System.CurrentTime();
}
if(result.success()){
diamond.setIsCut(true);
for(s:errorOrderList){
if(Sytem.CurrentTime - errorOrderTime <=1s)
{ handleErrorOrder(s)}
else{s.remove()}
}
}
}
2.3 思考
限流和熔断需要一起考虑,需要综合考虑哪种更适合业务,可以分布式计数和单机熔断一起使用,还要考虑
自动恢复策略,只有在实际业务场景使用,才能真正的让系统容灾能力更强,下次在熔断的基础上讲一下分布式限流算法应用在支付系统中以及各种限流算法的区别和具体实现