分布式系统的流量防卫兵:随着微服务的普及,服务调用的稳定性变得越来越重要。Sentinel以“流量”为切入点,在流量控制、断路、负载保护等多个领域开展工作,保障服务可靠性。
Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。Sentinel 同时提供实时的监控功能,在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。Sentinel 提供简单易用、完善的 SPI 扩展接口。可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
分布式系统面临的问题:复杂的体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败,比如如下的例子中,当我们调用A、E、F、J、K这几个服务的时候如果其中一个服务出现问题会造成什么问题?其实就会出现整体系统效率全部下降,而且严重就会出现服务雪崩的问题!
服务雪崩指的是,多个微服务之间调用的时候,假设A调用B和C,B和C又调用其他的微服务,这就是所谓的扇出。如果扇出的某个链路上某个微服务调用的响应时间过程或者不可用,微服务A的调用就用占用越来越多的系统资源,从而引起系统崩溃,这也就是服务雪崩。其实就是服务的高可用遭到了破坏。
对于高流量的应用来说,单一的后端依赖可能会导致服务器上的所有资源都在几秒钟内饱和。同时还有可能造成这些应用程序导致服务之间的延迟增加,备份列队,线程和其他的系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系失败,不能取消整个应用程序或系统,所以通常发生了一个模块的某个实例失败后,这时候这个模块依然还会接受流量,然后这个有问题的模块还调用其他的模块,这样就会发生级联故障,或者叫做雪崩。要解决这种问题的出现我们就需要用到服务降级,而Sentinel就可以保证在一个依赖出现问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性。
Sentinel的熔断降级通过断路器实现:断路器它本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似于熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法出的异常,这样就保证了服务调用方的不会被长时间、不必要的占用,从而避免了故障在分布式系统中蔓延(类似于病毒传染),从而避免了故障在系统中蔓延,乃至崩溃。
对比与其他的产品而言,比如说Hystrix,并不需要手动搭建监控平台,而且它有一套类似于Nacos的Web界面,可以让我们进行更加细粒度的配置流控、速率、服务熔断、服务降级等。目前主流编程都是约定>配置>代码,虽然配置都可以写在代码中,但是还是要大面积的学习配置和注解的方式,尽量少写代码,这也是Sentinel的理念和初衷。
Sentinel的官网地址:https://github.com/alibaba/Sentinel 直接下载对应版本的jar包即可
Sentinel 分为两个部分
- 核心库(Java客户端)不依赖任何框架/库,只需要Java运行时环境,同时对Dubbo/SpringCloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 SpringBoot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器。
启动步骤
- 前提:jdk1.8环境和8080端口不能被占用
- 启动命令:java -jar sentinel-dashboard-1.8.2.jar
- 访问地址:localhost:8080
- 输入默认账号密码:sentinel/sentinel
看到这个页面就说明Sentinel已经启动成功可以正式使用了。
Sentinel的官方文档网址:https://sentinelguard.io/zh-cn/docs/quick-start.html
在使用Sentinel之前,请先确保本地已经安装好Nacos并成功启动。Sentinel和Nacos这两大组件配合起来使用也是相辅相成,Sentinel来监控Nacos中注册的这些服务。如果在此之前没有接触过Nacos相关操作,可以先去参考这一篇文章:【手把手】教你玩转SpringCloud Alibaba之Nacos_FeenixOne的博客-CSDN博客
当以上的这些配置配置好以后,就可以进行测试了。在测试之前,首先保证Nacos和Sentinel都是启动状态,然后再来启动项目。
按照之前所介绍的这个时候,就应该在Sentinel的dashboard上能体现出它监控的服务。但是此时并没有,原因是因为Sentinel本身采用的是懒加载机制,所以我们需要首先访问服务对应的接口,Sentinel才能工作。
看到归看到,这个Sentinel到底起没起作用呢?很简单,来到testA和testB的页面疯狂刷新几次,模拟很多的请求过来,再来看看Sentinel的监控页面:
唯一名称,默认请求路径;
Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源);
- QPS(每秒钟的请求数量):当调用该API的QPS达到阈值的时候,进行限流;
- 线程数:当调用该API的线程数量达到阈值的时候,进行限流;
当前不需要集群;
- 直接:API达到限流条件时,直接限流;
- 关联:当关联的资源达到阈值时,就限流自己;
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)(API级别的针对来源);
- 快速失败:直接失败,抛异常;
- Wam Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFacotor,经过预热时长,才达到设置的QPS阈值;
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效;
可以在【簇点链路】中的【列表视图】下,针对某一个API直接设置流控规则,比如直接给"/testA"这个API配置流控规则:
单机阈值设定为1,代表的是当前这个接口1秒只能访问1次,超过这个阈值,就会被Sentinel阻塞,现在默认为直接失败。体现出来的效果就是:
改成并发线程数限制以后,再去疯狂刷新"/testA"这个API,发现好像没用啊,再开一个窗口再去疯狂的刷新,也没有任何报错,这是为啥呢?这么理解,在Sentinel做流控的时候,假设成是一个银行的网点的处理柜台。当设置成QPS阈值为1,也就是说1秒钟只能有1个人放进来柜台前进行业务处理;而设置成并发线程数阈值为1,所有人可以同时全部进来,但是柜台每次只能处理1个人业务,其余进来的人后面排队等着去。
想要模拟这种情况,就需要将"/testA"处理的时候线程休眠一会,重启项目:
项目重启以后,原先配置的流控规则会消息,需要重新配置"/testA"的线程数阈值:
多开几个页面,轮换着快速的刷新页面来模拟线程的争抢:
官方给出的说法是:当关联的资源达到阈值时,就限流自己。通俗解释来说:现在有"/testA"接口和"/testB"接口,当A关联的资源B达到阈值后,就限流自己,也就是B到达阈值,限流A本身。就好像孩子在外面打架,处理家长一个道理。换到程序里面来说,比如一个电商系统中,支付系统达到阈值,就限流下订单系统。
当【流控模式】选择"关联"之后,会自动弹出【关联资源】,直接填入相关联的接口即可。
使用PostMan模拟对"/testB"进行高并发访问:
可以简单理解成在0.2s内对接口进行50次访问
与此同时,在去手动访问"/testA"接口,就会发现此时"/testA"接口已经因为"/testB"接口的高并发访问而被控流了:
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。它的功能有点类似于针对来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细。比如在一个微服务中,两个接口都调用了同一个Service中的方法,并且该方法用@SentinelResource(用于定义资源)注解标注了,然后对该注解标注的资源(方法)进行配置,则可以选择链路模式。
写一个含有@SentinelResource注解的Service类
在Controller中调用Service中的方法
配置流控规则:这里要注意不要对"/testA"或者"/testB"进行限流规则的配置,要给用@SentinelResource注解标注的资源进行配置限流规则,这里的意思为当入口资源访问被@SentinelResource注解标注的资源方法时,当超过阈值就会被限流。
当【流控模式】选择"链路"之后,会自动弹出【入口资源】,直接填入相关联的接口即可。
修改yml配置文件
重新启动项目,项目重启之后原来在Sentinel中配置的流控规则也会消失,需要重新进行配置。此时再去疯狂刷新"/testB"接口,就会看到相应的报错信息
流控效果里有三个选项:
- 快速失败:直接失败,抛异常;
- 预热(warm up):根据coldFactor(冷加载因子,默认3)的值,从阈值/codeFacotor,经过预热时长,才达到设置的QPS阈值;
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效;
根据官网给出的说明:Warm Up方式,即预热/冷启动方式。该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过“冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。
预热公式:阈值/coldFactor(默认值为3),经过预热时间后才会达到阈值。
冷启动的过程系统允许通过的QPS曲线如下图:
一般秒杀系统中会有这样的流控设置,为了防止秒杀瞬间造成系统崩溃。默认coldFactor为3,当发起请求即请求QPS从(阈值/3)开始,经过多长预热时长才逐步升至设定的QPS阈值,当前阈值设置为10,预热时长设置为5秒。最终的效果就是,系统初始化时阈值/3取整为3,即阈值在此时为3,经过5秒后阈值才慢慢升高到10。
完成配置之后,再去疯狂刷新"/testA"接口,就能看到在瞬间大流量过来的时候,通过的请求随着时间的推演平滑缓慢的增长
匀速排队方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求,也就是所谓的削峰填谷。
Sentinel 匀速排队等待策略是漏桶算法结合虚拟队列等待机制实现的。它的中心思想是,以固定的间隔时间让请求通过。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过。否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。 不过需要注意的是,匀速排队模式暂时不支持 QPS > 1000 的场景。
配置流控效果
Controller打印一下相关信息
使用PostMan在瞬间发送很多请求,但是每秒钟就只处理一个请求。
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
Sentinel提供的熔断策略有:
- 慢调用比例 (`SLOW_REQUEST_RATIO`):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(`statIntervalMs`)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 异常比例 (`ERROR_RATIO`):当单位统计时长(`statIntervalMs`)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 `[0.0, 1.0]`,代表 0% - 100%。
- 异常数 (`ERROR_COUNT`):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
Sentinel在1.8.0之后对熔断降级做了大的调整,可以定义任意时长的熔断时间,引入了半开启恢复支持。熔断状态有三种状态:
状态 | 说明 |
---|---|
OPEN | 表示熔断开启,拒绝所有请求 |
HALF_OPEN | 探测恢复状态,如果接下来的一个请求顺利通过,则表示结束熔断,否则继续熔断 |
CLOSE | 表示熔断关闭,请求顺利通过 |
熔断降级规则包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用); 异常比例/异常数模式下为对应的阈值 |
|
timeWindow | 熔断时长,单位为 s | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值 也不会熔断(1.7.0 引入) |
5 |
statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
首先慢调用指的是,假设最大RT设置为300ms,当前来了个请求,这个请求响应的时间超过300ms,那么当前这个请求就会被标记为慢调用。
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
Controller中添加一个方法
设置熔断策略,1QPS>5 并且这些请求的RT>300 并且大于比例阈值触发熔断
使用Jmeter对"/testC"进行压力测试
此时再去手动访问"/testC"接口,发现已经被熔断
当停掉Jmeter的压测以后,立马访问也还是处于熔断状态,过10s之后才会重新恢复正常。
异常比例(ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。
新增控制器方法"/testD"
配置熔断策略
使用Jmeter进行测试,触发异常熔断
发现"/testD"已被熔断
异常数(ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。
设置异常数策略,当1秒钟内请求超过5并且异常数大约5个的时候触发熔断
使用Jmeter进行测试,触发异常熔断,发现"/testD"已被熔断
何为热点?热点即经常访问的数据。很多时候希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制;
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制;
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
其实这个热点限流其实就是更加细粒度的流控规则,那么如果想使用它就必须要配合对应SentinelResource注解。Sentinel 提供了 @SentinelResource 注解用于定义资源,它有很多的参数,主要关注两个参数:
1、value:代表资源名称,必需项,因为需要通过resource name找到对应的规则,这个是必须配置的;
2、blockHandler:对应处理 BlockException 的方法名称,可选项,访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException;
Controller中添加方法"/testHotKey"
配置热点规则
根据配置的规则,当传入的参数只要不是第1个参数,无论多疯狂的请求接口都可以正常访问
当传入的参数是第1个参数,只要超过1秒1次的请求,就会触发热点规则报错
在刚才的演示中,明显发现这种限流方法的提示效果非常不友好,所以如果需要能够得到友好的提示,就需要使用@SentinelResource注解提供的另外一个参数blockHandler,这个参数是可以指定当出现异常时的处理方法:
此时再触发热点规则后用户看到的提示就会相对比较友好:
其实@SentinelResource这个注解能做到的远远不止以上:
1、@SentinelResource 既可以配置资源名称也可以配置URL;
2、如果配置了@SentinelResource的blockHandler属性对应方法,出现限流会调用对应方法;
3、如果没有配置@SentinelResource的blockHandler属性,系统会走默认的限流处理;
但是在使用@SentinelResource注解这两种方案的时候,会发现一些问题:
1、没有体现我们自己的业务要求;
2、自定义处理方法和业务代码耦合在一起;
3、每个业务方法都添加一个限流处理方法,代码将会加剧膨胀;
4、无法实现统一全局处理;
其实@SentinelResource除了blockHandler可以设置自定义限流处理逻辑方法以外,还提供另外一个属性来设置限流处理逻辑类型blockHandlerClass属性,此属性中设置的方法必需为 static 函数,否则无法解析。
创建CustomerBlockHandler类型用于处理自定义限流处理逻辑:
在SentinelResourceTestController类型中添加一个接口方法,同时设置@SentinelResource注解和blockHandlerClass属性对应的类型和这个类型中对应的处理方法:
给bycustomer资源添加限流规则:
测试流控效果:
其实参数例外项就是可以达到更加细粒度的控制,比如在当前的例子中,目前hot1参数在访问时超过阈值就会被限流,但是可以通过参数例外项设置hot1具体等于特殊的某个值的时候,触发不同的限流效果。假如hot1的值等于5时,它的阈值可以达到200。注意:参数例外项中的参数类型仅支持一下7种数据类型:
配置热点规则,使得需要让hot1的值为5的时候阈值可以达到200
此时的规则为:如果当前hot1值为除5以外的其它值,都会走普通的阈值规则。但是如果一旦hot1的值为5的时候,将会走参数例外项,此时的阈值为200。通过浏览器测试,当hot1的值等于5是只要阈值不超过200就不会出现限流。
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量,比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持的模式:
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1(1分钟平均负载) 作为启发指标,进行自适应系统保护。当系统 load1(1分钟平均负载) 超过设定的启发值(阈值),且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps(秒级统计的最大QPS) * minRt(秒级统计的最小响应时间) 估算得出。设定参考值一般是 CPU cores * 2.5;
- CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏;
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒;
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护;
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护;
利用Ribbon进行负载均衡的调用,需要创建一个服务消费者cloudalibaba-consumer-8084和两个服务提供者cloudalibaba-provider-9003和cloudalibaba-provider-9004:
建立一个共享项目cloudalibaba-commons,在其中新建一个类型JsonResult,这个类型用于返回JSON数据类型:
新建cloudalibaba-provider-9003,引入nacos、commons相关依赖
编写application.yml配置文件
主启动类添加nacos注册注解
添加控制器Controller
重复相同步骤,创建cloudalibaba-provider-9004
新建cloudalibaba-consumer-8084,引入nacos、sentinel、commons相关依赖
编写application.yml配置文件
主启动类添加nacos注册注解
主启动类添加负载均衡Bean
添加控制器Controller
访问"localhost:8084/consumer/fallback/1"测试负载均衡是否成功:
fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常;
- fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析;
其实通过官网上提供的概念,不难看出这个属性类似于blockHandler,但是一定要注意它们有本质的不同。注意:fallback属性和blockHandler属性的本质不同在于他们作用的异常不同:
- blockHandler:违反Sentinel控制台配置规则时触发BlockException异常时对应处理的属性;
- fallback:针对Java本身出现的异常进行处理的对应属性;
添加异常,此时如果访问http://localhost:8084/consumer/fallback/4(id非法)地址时,就会出现对应的显示效果:
此时显示效果非常不好,我们就可以通过@SentinelResource注解的fallback属性来解决这种java异常,给出友好提示 :
它和blockHandler属性类似,也可以设置fallbackClass属性,来指定对应类型,来处理对应的Java异常,当然要注意和blockHandlerClass属性一样,也需要让所有的方法都必需为 static 函数,否则无法解析。
通过上述的内容,我们很清楚的知道了fallback属性的作用,但是大家现在想一个问题,如果我们在使用@SentinelResource属性的时候,同时设置blockHandler属性和fallback属性时,并且同时出现了Sentinel异常和Java异常,这个时候会执行哪个方法那。
我们还是回顾一下blockHandler属性的概念:
- blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
在Controller中添加blockHandler
设置Sentinel配置:在1秒内超过最小访问次数5次,并且异常数超过2的时候,会触发熔断规则:
此时再来访问http://localhost:8084/consumer/fallback/6看效果:
exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
访问页面,当出错的时候就可以看到指定忽略的异常没有被处理,原样被抛出
在Sentinel Dashboard中配置规则之后重启应用就会丢失,所以实际生产环境中需要配置规则的持久化实现,Sentinel提供多种不同的数据源来持久化规则配置,包括file、redis、nacos、zookeeper等。
而同为SpringCloud Aliabba组件的Nacos自然近水楼台先得月,成了持久化配置的首选。将限流规则持久化进Nacos保存,只要刷新某个接口地址,Sentinel控制台的流控规则就能感应到,同时只要Nacos里面的配置不删除,针对Sentinel的流控规则就持续有效。
其实就是实现Sentinel Dashboard与Nacos之间的相互通信,通过Nacos配置文件修改流控规则---拉取--->Sentinel Dashboard界面显示最新的流控规则。值得一提的是,在Nacos控制台上修改流控制,虽然可以同步到Sentinel Dashboard,但是Nacos此时应该作为一个流控规则的持久化平台,所以正常操作过程应该是开发者在Sentinel Dashboard上修改流控规则后同步到Nacos,遗憾的是目前Sentinel Dashboard不支持该功能。但是如果对Nacos的源码够熟悉的话,也是可以通过修改源码的方式来实现,后面有时间的话我会单独针对Nacos的源码出一篇讲解。
在8401项目中引入相关依赖:
修改yml文件配置持久化:
在Nacos控制台,添加相关配置:
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群;
在Controller中写一个测试Sentinel配置的方法:
启动8041项目,无须再去Sentinel中配置任何规则,直接疯狂访问localhost:8401/order/test1
可以看到已经被Sentinel控流了,证明配置在Nacos中的规则已经生效。登录Sentinel中,即可看到Nacos中的配置已经被同步到Sentinel中。
不过如上所说,这个功能是有缺陷的:如果想要修改Sentinel中的规则,必要要去Nacos中修改对应的配置文件,然后才会同步到Sentinel中。直接在Sentinel中修改自身的规则,并不会同步到Nacos的配置文件中。所以,Sentinel自身的规则居然不能在Sentinel中自身控制,真不知道设计者是怎么想的。后续有空讲解Sentinel源码的时候,再把这个问题拿出来解决掉。