熔断的基本认识
场景分析
在大型分布式架构中,一个用户的请求,可能是这样
如果这个时候一个服务出现异常
服务提供者不可用(硬件故障、程序bug、网络故障、用户请求量较大) 重试导致的流量过大
服务调用者使用同步调用,产生大量的等待线程占用系统资源,一旦线程资源被耗尽,调用者提供 的服务也会变成不可用状态
就会导致请求堆集从而出现整个服务不可用的问题。用古话来讲就是:千里之堤毁于蚁穴
在复杂的分布式架构的应用程序有很多的依赖,都会不可避免的出现服务故障等问题。高并发的依赖失 败时如果没有隔离措施,当前应用服务就有被拖垮的风险。
引入熔断机制
在分布式架构中,有一种解决方法,就是熔断机制。
也就是说当下游服务因为访问压力过大或者其他原因导致响应变慢的时候,上游服务为了保护自己以及 系统整体的可用性,可以暂时切断对于下游服务的调用。
熔断在生活中也随处可见,
1.比如“跳闸”,当电压超过负荷时,开关会自动跳闸。从而防止出现电路烧毁带来的火灾。
2.比如股票市场的熔断,对于股票设置一个熔断价格,当价格触发到熔断点之后,交易会被暂停一段 时间。或者交易可以继续进行,但是报价会限制在一定的范围
那生活中的这种场景,能不能应用在架构设计中呢?
思考熔断机制的实现设计
那么大家在思考一下,如过项目中要实现熔断,是不是也需要像股票或者电闸这种设计一样,需要设置 一个阈值呢?
大家会发现,架构是基于人的架构,所以架构的设计都是基于人对于事务的基本认识来实施的。因此越 往后面学习,越能够发现很多设计思想都来自于生活。
Sentinel熔断降级
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后, 在接下来的降级时间窗口之内,对该资源的调用都自动熔断
那么怎么去判断资源是否处于稳定状态呢?
1.平均响应时间,比如,在1s内连续处理5个请求,它的平均响应时间都超过阈值,那么在后续的时 间窗口中,对于这个方法的调用都会自动熔断,sentinel默认的平均响应时间是4900ms
2.异常比例,当指定资源每秒请求量大于等于5,并且每秒的异常总数占通过量的比值超过阈值之后
(比如每秒处理1000个请求,那么其中异常请求数为500,那么当前的比值是50%),那么该资源 会进入降级状态。异常的比率范围是[0.0.1.0]表示0%到100%
3.异常数,当资源在1分钟的异常数据超过阈值后会进行熔断
针对这些规则,Sentinel中给出了响应的字段来设置平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。
异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= 5,并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口
(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。
熔断演示
Jar包依赖
在这个案例中,我们可以基于前面讲解的dubbo服务进行改造,只需要依赖这个jar,
配置规则
添加一个DataSourceInitFunc。 然后在resource/META-
INF/services/com.alibaba.csp.sentinel.init.InitFunc中配置改类的全路径,这样的话sentinel在触发限流时会去调用这个initFunc来解析规则
public class DataSourceInitFunc implements InitFunc {
@Override
public void init() throws Exception { List rules=new ArrayList<>(); DegradeRule rule=new DegradeRule();
//下面这个配置的意思是,当1s内持续进入5个请求,平均响应时间都超过count(10ms),
// 那么在接下来的timewindow(10s)内,对
//这个方法的调用都会自动熔断,抛出异常:degradeException.
//指定被保护的资源
rule.setResource(“com.gupaoedu.sentinel.SentinelService”); rule.setCount(10); //阈值
//降级模式, RT(平均响应时间)、异常比例(DEGRADE_GRADE_EXCEPTION_RATIO)/异常数
量
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
rule.setTimeWindow(10);//降级的时间单位, 单位为s rules.add(rule); DegradeRuleManager.loadRules(rules);
}
}
修改SentinelServiceImpl
增加一个sleep,这个时候就会起到一个熔断的效果
@Override
public String sayHello(String name) { try {
Thread.sleep(1500); //添加这个和不添加这个的影响
} catch (InterruptedException e) { e.printStackTrace();
}
System.out.println(“begin execute sayHello:”+name);
return “Hello World:”+name+"->timer:"+ LocalDateTime.now();
}