栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

重学SpringCloud系列八之分布式系统流量卫兵sentinel

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

重学SpringCloud系列八之分布式系统流量卫兵sentinel

重学SpringCloud系列八之分布式系统流量卫兵sentinel

sentinel简介与安装

一、Sentinel 是什么?

Sentinel 分为两个部分: 二、单机版下载安装

下载启动开放防火墙端口登录 客户端集成与实时监控

二、微服务集成Sentinel客户端 实战流控规则-QPS限流

一、如何添加流控规则

QPS流控 二、限流效果测试 实战流控规则-线程数限流

一、线程数限流二、流控效果测试 实战流控规则-关联限流

一、关联限流二、流控效果测试 实战流控规则-链路限流

一、什么是链路限流二、将一个服务层函数标记为资源三、增加流控规则四、但是 实战流控效果-WarmUp

一、什么是Warm Up二、如何配置Warm UP三、测试一下 实战流控效果-匀速排队

一、什么是匀速排队二、测试效果

2.1.请求超时0.5秒(上文图中配置)2.2.将超时时间调大为5秒 BlockException处理

一、BlockHandler二、blockHandlerClass与BlockHandler搭配适用三、总结 实战熔断降级-RT

一、RT降级策略

问题:为什么要1秒超时5次才降级? 二、RT熔断降级测试三、sentinel流控规则与降级规则的区别 实战熔断降级-异常数与比例

一、异常比例降级

测试: 二、异常数量降级

测试: DegradeException处理

一、fallback方法二、fallback与fallbackClass结合使用三、blockHolder与fallback关系

1.1.业务异常测试1.2.资源触发降级规则 四、通用降级处理方法 注解与异常的归纳总结

一、区分流控规则及降级规则二、异常处理关系图三、fallback与blockHandler用法总结

笔者自身的最佳实践(不绝对) 四、SentinelResource注解用法总结 Feign降级及异常传递拦截

一、在FeignClient接口配置fallback降级二、远程服务调用异常传递的问题三、sentinel-feign异常拦截与处理

3.1.第一种处理方式3.1.暂时没有第二种处理方式 动态规则nacos集中存储

一、前言二、架构三、Sentinel DashBoard控制台代码调整四、DashBoard打包部署五、微服务客户端配置六、发布流控配置 热点参数限流

一、何为热点参数限流二、热点参数接口三、配置热点参数限流基础规则

3.1.快速点击测试(携带orgNameLike参数)3.2.快速点击测试(不携带orgNameLike参数) 四、配置参数例外项目 系统自适应限流

一、系统规则二、如何查看linux系统的运行信息


sentinel简介与安装 一、Sentinel 是什么?

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 分为两个部分:

核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。在一定程度上可以将Sentinel Java客户端理解为“流量防火墙”控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。Sentinel控制台不仅能展示服务流控、熔断降级相关的数据,还可以通过配置的方式动态的为Sentinel客户端下发流量控制的指令。 二、单机版下载安装

Sentinel Dashboard作为一个监控控制台,只是作为信息收集展示和命令发送端来使用。它的运行并不会真正的影响业务服务,通常不需要集群部署。

下载

我们需要下载并安装的是DashBoard控制台,下载地址:https://github.com/alibaba/Sentinel/releases。下载如下图所示的sentinel-dashboard-x.y.z.jar

启动

注意:启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。

使用如下命令启动控制台:

nohup java -Dserver.port=8774 
-Dcsp.sentinel.heartbeat.client.ip=192.168.161.3 
-Dproject.name=sentinel-dashboard -jar 
sentinel-dashboard-1.7.2.jar &

-Dserver.port=8080用于指定 Sentinel 控制台端口为8774。默认是8080。我们给它改成不常用的端口。-Dcsp.sentinel.heartbeat.client.ip=192.168.161.3 控制台部署的地址,指定控制台后客户端会自动向该地址发送心跳包。 (多网卡环境下如果不做这个配置,会报出连接超时的异常)-Dproject.name=sentinel-dashboard 指定Sentinel控制台程序的名称

从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是sentinel。当然也可以通过JVM参数的方式进行修改。

-Dsentinel.dashboard.auth.username=sentinel用于指定控制台的登录用户名为sentinel;-Dsentinel.dashboard.auth.password=123456用于指定控制台的登录密码为123456;如果省略这两个参数,默认用户和密码均为sentinel;-Dserver.servlet.session.timeout=7200用于指定 Spring Boot 服务端 session 的过期时间,如7200表示 7200 秒;60m表示 60 分钟,默认为 30 分钟;

Sentinel本身就是一个Spring Boot应用,所以修改Jar包内部的application.properties文件也是可以修改配置的。

开放防火墙端口

如果是部署在linux机器上,如CentOS7需要开放防火墙端口:

firewall-cmd --zone=public --add-port=8774/tcp --permanent 
firewall-cmd --reload
登录

访问:http://192.168.161.3:8774

暂时空空如也


客户端集成与实时监控

当前客户端是基于以下内容之上:

正确集成了OpenFeign(Ribbon)及其配置正确集成了nacos及其配置

可以依据自己的项目,决定是否需要引入上面的框架


二、微服务集成Sentinel客户端

通过maven坐标在微服务模块aservice-rbac、aservice-sms中加入sentinel客户端


    com.alibaba.cloud
    spring-cloud-starter-alibaba-sentinel

在项目的配置中加上sentinel配置,因为我是用了nacos,所以去nacos修改配置文件。如果你的服务没有使用配置中心,在application.yml里面配置就可以了。

spring:
  cloud:
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        port: 8719
        #Sentinel会启动一个http server和dashboard进行通信,而这个server默认占用端口是9719
        #假如8719端口被占用,会自动+1,直到找到没有被占用的端口为止
        dashboard: 192.168.161.3:8774  

只需要完成上述的配置,代码不需要有任何的调整,我们就可以通过实时监控查看服务内的流量QPS以及平均响应时长等信息。

需要注意的是只有服务接口被访问的情况下,在sentinel里面才可以看到监控信息。


下节,我们在此基础之上为大家介绍sentinel的流量控制!


实战流控规则-QPS限流 一、如何添加流控规则

在菜单左侧的“簇点链路”和流控规则都可以针对“服务接口”添加流控规则

当我们的服务接口资源被访问的时候,就会出现在“簇点链路”列表中,我们可以针对该服务接口资源配置流程控制规则。在“簇点链路”还可以配置降级规则、热点以及授权(后文章会讲到)
在流控规则页面也有“新增流控规则”按钮,添加完成之后的流控规则,出现在流控规则页面列表中。
QPS流控

点击“新增流控规则”按钮之后,弹出如下的配置页面。本节以QPS限流为例为大家讲解流控规则的配置

资源名称:表示我们针对哪个接口资源进行流控规则配置,如:“/sysuser/pwd/reset”针对来源:表示针对哪一个服务访问当前接口资源的时候进行限流,default表示不区分访问来源。如填写服务名称:aservice-xxxx,表示aservice-xxxx访问前接口资源的时候进行限流,其他服务访问该接口资源的时候不限流。阈值类型/单机阈值:QPS,每秒钟请求数量。上图配置表示每秒钟超过1次请求的时候进行限流。流控模式:直接,当达到限流标准时就直接限流流控效果:快速失败。很简单的说就是达到限流标准后,请求就被拦截,直接失败。(HTTP状态码:429 too many request)是否集群:默认情况下我们的限流策略都是针对单个服务的,sentinel提供了集群限流的功能。笔者个人意见是:除非你的微服务规模特别大,一般不要使用集群模式。集群模式需要各节点与token server交互才可以,会增加网络交互次数,一定程度上会拖慢你的服务响应时间。

上面的限流规则用一句话说:对于任何来源的请求,当超过每秒1次的标准之后就直接限流,访问失败抛出异常(BlockException)!

其他的限流配置我们后面文章再为大家讲解。

二、限流效果测试

使用Postman向“/sysuser/pwd/reset”发送请求,慢点点击发送(一秒一次),返回正常结果:

使用Postman向“/sysuser/pwd/reset”发送请求,快点点击发送(超过一秒一次),返回结果如下:

说明QPS限流规则生效,被限制的请求直接返回失败数据!


实战流控规则-线程数限流 一、线程数限流

资源名称:表示我们针对哪个接口资源进行流控规则配置,如:“/sysuser/pwd/reset”针对来源:表示针对哪一个服务访问当前接口资源的时候进行限流,default表示不区分访问来源。如填写服务名称:aservice-xxxx,表示aservice-xxxx访问前接口资源的时候进行限流,其他服务访问该接口资源的时候不限流。阈值类型/单机阈值:线程数。表示开启n个线程处理资源请求。流控模式:直接,当所有线程都被占用时,新进来的请求就直接限流流控效果:快速失败。很简单的说就是达到限流标准后,请求就被拦截,直接失败。(HTTP状态码:429 too many request)

上面的限流规则用一句话说:对于任何来源的请求,aservice-rbac服务端“/sysuser/pwd/reset”资源接口的2个线程都被占用的时候,其他访问失败!

二、流控效果测试

参考Hystrix章节的《Jemeter模拟触发服务熔断》创建Jemeter接口测试用例。为了更明显的触发流控规则:配置一秒钟发送30个请求。

sentinel控制台线程数单机阈值=1,aservice-rbac服务开启1个线程处理“/sysuser/pwd/reset”资源请求,所以30 * 5/6的请求被限流。sentinel控制台线程数单机阈值=2,aservice-rbac服务开启2个线程处理“/sysuser/pwd/reset”资源请求,所以30 * 4/6的请求被限流。sentinel控制台线程数单机阈值=3,aservice-rbac服务开启3个线程处理“/sysuser/pwd/reset”资源请求,所以30 * 3/6的请求被限流。

下图中绿色表示请求成功,红色表示请求失败(被限流)!

我的测试结果比较圆满,很景精确的解释了线程数与限流请求数之间的关系。但是在实际生产环境下,环境、网络等原因流控结果不一定是如此精确的和线程数成比例的。


实战流控规则-关联限流 一、关联限流

关联限流:针对B接口配置关联限流规则,当B接口配置关联限流规则达到标准时, 关联资源A接口访问被限流。

上图的配置表示:

对关联资源接口“/sysuser/pwd/reset”使用QPS的限流规则,每秒钟只处理一个请求。(这个规则只是一个统计标准,并不会对“/sysuser/pwd/reset”真的限流)当大量的并发请求达到“/sysuser/pwd/reset”关联资源接口的限流标准的时候,“/sysrole/query”资源将被限流。流控效果是快速失败。

需要注意的是:

在关联限流配置中,虽然我们对关联资源“/sysuser/pwd/reset”进行了限流规则配置,但该配置对“/sysuser/pwd/reset”并不生效。sentinel会统计请求流量,根据流量是否触发关联资源“/sysuser/pwd/reset”的限流标准,去限制“/sysrole/query”资源。

大家注意不要把限流关系弄反了!限流规则是为了限制“资源”,而不是“关联资源”!

关联的资源如果并发访问量过大,自己先挂了,关联对象一点事没有,舍己为人

二、流控效果测试

参考Hystrix章节的《Jemeter模拟触发服务熔断》创建Jemeter接口测试用例。为了更明显的触发流控规则:配置10秒钟发送300个请求“/sysuser/pwd/reset”。之所以10秒钟是为了给我们留出去操作访问“/sysrole/query”接口的时间。

针对“/sysuser/pwd/reset”10秒300请求,虽然为该接口配置限流规则(1秒1次),但是访问并未失败(下图中的绿色盾牌表示访问成功,并没有被限流)
与此同时访问“/sysrole/query”资源接口,被限流了!


实战流控规则-链路限流 一、什么是链路限流


如上图所示

我们针对getUserByUserName资源进行流控规则配置,入口为:"/sysuser/info”。期望实现的效果是从"/sysuser/info”访问getUserByUserName资源被限流,从“/sysorg/tree”入口访问getUserByUserName资源不被限流。 二、将一个服务层函数标记为资源

在SysuserService方法中添加@SentinelResource注解将该函数标记为资源,成为资源之后,就可以为它添加流控规则。

Controller层方法,不用加@SentinelResource注解,默认就被标记为资源。

资源添加完成之后,从“簇点链路”页面看,资源调用呈现树形结构。

三、增加流控规则


上面的规则配置的含义是:

从"/sysuser/info”访问getUserByUserName资源被限流,从其他入口访问getUserByUserName资源不被限流。限流规则是:1秒钟只允许访问一次,超出之后访问失败。 四、但是

从网友使用的反馈情况来看,链路流控规则的使用情况并不是很稳定。很多网友反映以上的流控规则并不生效。笔者经过反复实验,我是用的sentinel 1.7.2版本的确是无法生效,所以这一节内容暂时放在这里!后续有发展我们再继续更新!

github上面网友提出的issue,官方并未关闭。(如果是关闭状态,说明这个问题已经被解决了)
https://github.com/alibaba/spring-cloud-alibaba/issues/1275其他网友反应的问题。
https://coding.imooc.com/learn/questiondetail/159466.html


实战流控效果-WarmUp

之前的章节主要为大家介绍流控规则的配置,其中流控规则中的流控效果配置有三种

快速失败:就是流量达到阀值或线程量占满,直接返回错误报异常Warm Up:在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间(本节为大家介绍)匀速排队:让请求以均匀的速度通过,下一节为大家介绍 一、什么是Warm Up

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统流量长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

为什么冷系统容易被压垮?
一般在我们系统内部会有线程池,比如:数据库连接线程池。在系统较为空闲的时候,数据库连接线城池内只有少量的连接。假设突然大量的请求并发而至,数据库连接池会去创建新的连接,用来支撑高并发请求。但是这个连接创建的过程需要时间,有可能这边连接池内新连接还没创建完成,这些少量的连接支撑不住就会被压垮。

所以在类似这种场景下,Warm Up让流量缓慢爬升,从而给数据库连接池创建连接一个缓冲的时间,就显得非常有必要了!

二、如何配置Warm UP

Warm Up配置有三个要素:

冷启动因子coldFactoer,默认等于3预热时长(配置项)QPS单机阈值(配置项)


举例:当预热时长=8,QPS单机阈值=3

当并发请求到达的时候,实际的单机阈值是:QPS单机阈值配置/coldFactoer=3/3=1,也就是每秒钟只能一个请求访问成功。预热时长为8秒,实际的单机阈值在8秒钟内逐步由1 -> 2 -> 3,最终等于QPS单机阈值配置。 三、测试一下

参考Hystrix章节的《Jemeter模拟触发服务熔断》创建Jemeter接口测试用例。为了更明显的触发流控规则:配置20秒钟发送60个请求“/sysuser/pwd/reset”。

红色表示请求失败(被限流),绿色表示请求成功

大家从上面的测试结果可以看出:

冷启动初期,三个请求能成功一个(单机阈值=1)中期,三个请求能成功2个(单机阈值=2)后期WarmUp限流配置到达阈值3的时候,所有的请求都能被成功处理(单机阈值=3)


实战流控效果-匀速排队 一、什么是匀速排队

匀速排队:就是让请求以均匀的速度通过,阈值类型必须是QPS。

上图的配置表示的是:“/sysuser/pwd/reset”资源服务接口,每秒钟匀速通过2个请求。当每秒请求大于2的时候,多余的请求排队等待,等待的时间是500ms。如果500ms以内请求得不到处理,就被限流访问失败!

二、测试效果

参考Hystrix章节的《Jemeter模拟触发服务熔断》创建Jemeter接口测试用例。为了更明显的触发流控规则:配置20秒钟发送60个请求“/sysuser/pwd/reset”。

2.1.请求超时0.5秒(上文图中配置)


从上图的请求结果可以看出:60个请求20秒发完,平均一秒3个请求,我们配置的匀速通过阈值是2。所以每秒处理2个请求,另外一个请求等待之后超过超时时间0.5秒(500ms),访问失败!

2.2.将超时时间调大为5秒

刚才我们的超时时间为0.5秒,所以请求很容易就超时了。现在将超时时间修改为5秒,再次发送请求。

请求刚开始发送的时候,我们配置的匀速通过阈值是2,所以每秒处理2个请求。先发送的请求先进入排队队列,在5秒之内发送的请求几乎都被成功处理了,后来队列里面的请求积压的越来越多,导致后面不断有请求超时(超过5秒)。


BlockException处理

在前面的若干节内容中为大家介绍了sentinel流控规则的配置,只要我们正确的集成了sentinel客户端(核心库),我们几乎不需要做任何的代码开发。就可以实现应用服务限流。

当请求并发达到限流的标准之后,我们再请求资源服务接口,就会得到上文中的响应结果。原始的响应结果实际包含了三部分内容:

HTTP状态码:429 too many request,太多的请求无法处理提示信息:Blocked by Sentinel (flow limiting),请求被Sentinel的限流策略拦截后台服务实际上抛出了BlockException(准确的说是FlowException继承自BlockException)

其实从笔者自身的实践来看,把上面的原始的限流响应结果交给前端做一个统一的处理,转换成友好的提示信息比如:“系统服务繁忙请稍后再试!”。是非常好的做法:统一、简单、易维护! 但是java服务端也给出了BlockException(准确的说是FlowException继承自BlockException)响应的异常拦截处理方案,我们也有必要学习一下。以备适应更多的个性化场景。

一、BlockHandler

如果我们希望请求被限流之后,给用户一些相对友好的信息,而不是Blocked by Sentinel (flow limiting);或者服务被限流之后做一些特殊的业务处理。我们需要通过SentinelResource注解的BlockHandler属性来实现。

value:资源名称,必需项。需要注意的是:如果controller的mappingUrl是"/sysuser/pwd/reset",那么SentinelResource的value属性就不要再配置成"/sysuser/pwd/reset"。会导致异常等处理逻辑偶尔失效。blockHandler :当资源服务接口被限流之后,就执行该属性指定的方法。

对应处理 BlockException 的方法名称,可选项。blockHandler 方法修饰符必须是 public,返回类型需要与原方法相匹配。参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。


上面的代码+控制台资源访问限流配置(QPS=1),然后我们使用Postman快速向"/sysuser/pwd/reset"发送请求,结果如下(不再是系统默认的限流响应信息,对于前端更加友好):

二、blockHandlerClass与BlockHandler搭配适用

按照上面的使用BlockHandler进行限流之后的异常处理,blockHandler 方法和项目的实际业务处理方法在一个类中,如果我们希望把限流处理方法独立出来,需要使用到blockHandlerClass。

我们可以把通用的blockHandler 异常处理方法单独抽取到一个类中

方法必须是static否则无法正确被解析方法参数定义必须和实际业务处理方法一致,在此基础上新增一个BlockException参数

代码如下:

public class SysuserControllerHandler {

  public static AjaxResponse pwdresetBlockHandler(@RequestParam Integer userId,
                                             BlockException blockException) {
    return AjaxResponse.error(CustomExceptionType.SYSTEM_ERROR,
            "尊敬的客户您好,系统服务繁忙,请稍后再试!");
  }

}

在实际的业务处理方法上,blockHandlerClass与BlockHandler搭配适用找到限流之后的处理方法。

上面的代码+控制台资源访问限流配置(QPS=1),然后我们使用Postman快速向"/sysuser/pwd/reset"发送请求,结果如下(不再是系统默认的限流响应信息,对于前端更加友好):

三、总结

大家可以看到使用blockHandlerClass虽然可以达到实际业务处理方法与blockHandler 方法解耦目的,但是我们仍然需要几乎针对每一个函数进行限流方法的开发,因为每个方法的参数是不同的。所以这些方法都只适用于个性化配置,不是全局配置。

那么sentinel针对服务限流,有没有全局的默认的BlockHandler呢?答案是没有(2020年5月)。但是从笔者的实践来看这真的不影响什么,把最原始的限流响应结果交给前端做一个统一的处理,给出友好的提示信息比如:“系统服务繁忙请稍后再试!”。是非常好的做法:统一、简单、易维护!


实战熔断降级-RT

Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。

一共有三种熔断降级策略:

RT平均响应时长异常比例异常数量

先为大家介绍第一种:RT平均响应时长降级策略。

一、RT降级策略


以下的两个条件同时满足才能触发RT服务降级。

一秒钟内通过5个以上的请求,全部超时超时是指超过RT平均响应时长的配置值(上图红色框配置)

触发RT服务降级规则之后,在时间窗口期内(上图绿色框配置)对资源的访问将被降级,即:断路器被打开。时间窗口期之后关闭断路器。

需要注意的是:

    RT平均响应时长最大可配值为4900毫秒。当配置超过4900的时候,默认等于4900。官方之所以设置RT最大值为4900,是因为当一次请求超过5秒就到了用户能忍受的极限。如果确实希望修改RT可配最大值,使用-Dcsp.sentinel.statistic.max.rt=xxx进行配置
问题:为什么要1秒超时5次才降级?

答:如果一个服务的请求量比较小,几秒钟才有一次请求,不巧这1次请求因为瞬时网络原因失败了,进而导致服务降级,这种情况是我们不愿意看到的。服务降级仍然是主要指针对高并发情况下,导致的服务资源紧张的情况下生效。

不能因为有一例新冠患者,就进行封城!同样道理,不能因为个别情况,就进行服务降级!minRequestAmount=5秒钟,是在DeGradeRule中的配置RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT。

二、RT熔断降级测试

为了更容易触发降级,我们将平均响应时长设置为0.1秒(我们的方法执行时间肯定大于0.1秒)。降级之后的时间窗口是10秒。

参考Hystrix章节的《Jemeter模拟触发服务熔断》创建Jemeter接口测试用例。为了更明显的触发降级规则:配置1秒钟发送10个请求“/sysuser/pwd/reset”。

下图为服务熔断降级之前的响应结果。需要注意的是:虽然超过了0.1秒平均响应时长,但是可以正常响应结果。
下图为服务熔断降级之后的响应结果,已经超过5次(一秒内发送的),所有请求处理均超过100ms。sentinel对于异常请求的临界状态判断并未执行的十分严格,有的时候是在第6次请求之后才进入服务降级。

哎,大家看“尊敬的客户您好,系统服务繁忙,请稍后再试!”这个响应结果是不是有点眼熟?没错,它就是我们上一节BlockHandler的处理方法。sentinel服务降级之后,有2种处理方法:

fallback方法(defaultFallback方法)处理DegradeException,即:处理服务熔断降级。fallback服务降级处理方法我们后面的章节讲!当服务降级没有定义fallback方法的时候,就会去执行BlockHandler方法。二者都定义了的话,也是执行BlockHandler方法。BlockHandler优先 三、sentinel流控规则与降级规则的区别

初学者咋一看sentinel流控规则与降级规则,好像没什么区别啊?为什么不统一叫做规则配置?还要去分开呢?为了方便大家的理解,我们来比较一下。

比较点流控规则降级规则
内容比较QPS、线程数、关联、链路限流、冷启动及匀速排队平均响应时长、异常比例、异常数量
解决问题的方向外部流量压力导致问题内部编码及处理能力导致的问题
ExceptionFlowException(BlockException)DegradeException(BlockException)
降级处理方法BlockHandlerfallback或BlockHandler

实战熔断降级-异常数与比例

本节为大家介绍降级规则的另外两种:异常比例和异常数量。首先我们人为的在代码中加入一个被除数为0的运行时异常。

一、异常比例降级


异常比例降级的触发条件:

资源的每秒请求数量>=5请求异常响应的数量占总请求数量的超过“异常比例配置”

当以上的两个条件都满足的时候,资源进入降级状态。在接下来的“时间窗口”内的请求都将被降级,执行fallback方法或者BlockHandler方法。

测试:

参考Hystrix章节的《Jemeter模拟触发服务熔断》创建Jemeter接口测试用例。为了更明显的触发降级规则:配置1秒钟发送10个请求“/sysuser/pwd/reset”。

从以上响应结果可以看出:在5次异常请求之后(被除数为0),异常请求占比100%(大于0.5),资源接口进入降级状态执行BlockHandler方法。BlockHandler方法参看《BlockException处理》和《实战熔断降级-RT》中的定义。

二、异常数量降级

明白了异常比例降级,异常数量降级就不难理解了。 异常数量降级的触发条件:近1分钟内异常请求数量超过“异常数配置”,资源进入降级状态。在接下来的“时间窗口”内的请求都将被降级,执行fallback方法或者BlockHandler方法。

需要注意的是:时间窗口的配置必须大于等于60,否则将导致结束熔断状态之后,再次无缘无故进入熔断状态。

测试:

参考Hystrix章节的《Jemeter模拟触发服务熔断》创建Jemeter接口测试用例。为了更明显的触发降级规则:配置1秒钟发送10个请求“/sysuser/pwd/reset”。

从以上响应结果可以看出:在6次异常请求之后(被除数为0),资源接口进入降级状态执行BlockHandler方法。BlockHandler方法参看《BlockException处理》和《实战熔断降级-RT》中的定义。


DegradeException处理 一、fallback方法

fallback 函数用于两种场景下提供 fallback处理逻辑:

业务上抛出运行时异常的时候资源触发降级规则的时候

如:下面我们认为制造了被除数为0的异常,会执行fallback方法:pwdresetFallback给出响应结果。

返回值类型必须与原函数返回值类型一致;方法参数列表需要和原函数一致,或者可以额外多一个Throwable类型的参数用于接收对应的异常。 二、fallback与fallbackClass结合使用

fallback 函数默认需要和原方法在同一个类中。若希望将fallback函数与业务解耦,将其单独拆分到一个类中,则可以指定fallbackClass为对应的类的Class对象。

fallbackClass需要和fallback结合一起使用,注意对应的函数必需为 static 函数,否则无法解析

public class SysuserControllerHandler {
  public static AjaxResponse pwdresetFallback(@RequestParam Integer userId,Throwable e) {
    return AjaxResponse.error(CustomExceptionType.SYSTEM_ERROR,
            "尊敬的客户您好,系统服务繁忙,请稍后再试!(pwdresetFallBack)");
  }
}
三、blockHolder与fallback关系

需要特别的注意:当blockHandler和fallback方法同时定义,且资源触发的降级规则时候,降级处理逻辑由blockHandler来执行。这种情况下,blockHandler优先级高于fallback方法。

1.1.业务异常测试

人为制造上面代码中的被除数为0异常,通过Postman发送请求结果如下:

执行的是fallback方法,业务上抛出运行时异常的时候由fallback方法处理。与blockHandler无关!

1.2.资源触发降级规则

使用jmeter测试,当降级规则被触发之后,执行的是blockHandler,而不是fallback。这种情况下,blockHandler优先级高于fallback方法。

把blockHandler删除之后,再次使用jmeter测试,当降级规则被触发之后,执行的是fallback。

四、通用降级处理方法

defaultFallback(since 1.6.0):通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。

返回值类型必须与原函数返回值类型一致;方法参数列表需要为空,或者可以额外多一个Throwable类型的参数用于接收对应的异常。defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定fallbackClass为对应的类的Class对象,注意对应的函数必需为 static 函数,否则无法解析。

public class SysuserControllerHandler {
  public static AjaxResponse defaultFallback() {
    return AjaxResponse.error(CustomExceptionType.SYSTEM_ERROR,
            "尊敬的客户您好,系统服务繁忙,请稍后再试!(defaultFallback)");
  }
}

只要返回值类型一致,在任何的资源上都可以使用defaultFallback,通用!

若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。个性化配置大于通用配置!


注解与异常的归纳总结 一、区分流控规则及降级规则

这部分虽然我们之前就说过,本节我们有必要再看一下。能正确的区分流控规则与降级规则,是理解Exception处理的基础。初学者咋一看sentinel流控规则与降级规则,好像没什么区别啊?为什么不统一叫做规则配置?还要去分开呢?为了方便大家的理解,我们来比较一下。

比较点流控规则降级规则
内容比较QPS、线程数、关联、链路限流、冷启动及匀速排队、热点规则限流平均响应时长、异常比例、异常数量
解决问题的方向外部流量压力导致问题内部编码及处理能力导致的问题
ExceptionFlowException(BlockException)DegradeException(BlockException)
降级处理方法BlockHandlerfallback或BlockHandler
二、异常处理关系图

我们先后已经为大家介绍了sentinel的流控规则和熔断降级,流控规则被触发之后,程序将去执行BlockHolder方法。那么熔断被触发之后,程序去执行什么方法呢?sentinel为我们提供了一个专门处理服务降级的方法:fallback。

正如上图所示:

FlowException代表限流规则被触发DegradeException代表熔断降级规则被触发FlowException和DegradeException都是BlockException的子类BlockHandler是可以用来被处理限流及熔断降级的方法fallback用来处理DegradeException,即处理熔断降级的方法fallback不仅可以用来处理熔断降级,还可以用来处理业务上的运行时异常 三、fallback与blockHandler用法总结

针对资源触发限流规则:只有blockHandler 能自定义处理逻辑针对业务上抛出运行时异常:只有fallback和defaultFallback能自定义处理逻辑。fallback 优先级高于 defaultFallback针对资源触发降级规则:blockHandler > fallback > defaultFallback 笔者自身的最佳实践(不绝对)

不自定义blockHandler,限流信息直接返回给前端处理。前端将限流信息转换成如:“系统繁忙,请稍后再试!”对于大部分的业务,使用defaultFallback定义全局通用降级处理逻辑。如:“系统内部出现错误,请联系管理员进行处理!”对于重点业务,降级逻辑个性化较强的业务,比如需要返回缓存。这种情况使用fallback。一旦使用fallback就用fallbackClass,一旦使用blockHolder就用blockHolderClass,降低代码耦合 四、SentinelResource注解用法总结

注意:SentinelResource注解方式不支持 private 方法。

@SentinelResource用于定义资源,并提供可选的异常处理和 fallback 配置项。@SentinelResource注解包含以下属性:

value:资源名称,必需项(不能为空)

entryType:entry 类型,可选项(默认为EntryType.OUT)

blockHandler/blockHandlerClass:blockHandler对应处理BlockException的函数名称,可选项。blockHandler 函数访问范围需要是public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定blockHandlerClass为对应的类的Class对象,注意对应的函数必需为 static 函数,否则无法解析。

fallback/fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:

返回值类型必须与原函数返回值类型一致;

方法参数列表需要和原函数一致,或者可以额外多一个Throwable类型的参数用于接收对应的异常。

fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定fallbackClass为对应的类的Class对象,注意对应的函数必需为 static 函数,否则无法解析。

defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了

exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:

返回值类型必须与原函数返回值类型一致;

方法参数列表需要为空,或者可以额外多一个Throwable类型的参数用于接收对应的异常。

defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定fallbackClass为对应的类的Class对象,注意对应的函数必需为 static 函数,否则无法解析。

exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理。


Feign降级及异常传递拦截 一、在FeignClient接口配置fallback降级

参考《服务熔断降级hystrix》章节中的《Hystrix结合Feign服务降级》学习,二者的实现方法及目的是一样的,所以此处简写,只写不同的部分。

在服务配置文件中打开feign结合sentinel的开关

feign:
  sentinel:
    enabled: true

在FeignClient注解增加fallback处理实现类,如:SmsServiceFallback。

@FeignClient(name="ASERVICE-SMS",fallback = SmsServiceFallback.class)
public interface SmsService {

  @PostMapping(value = "/sms/send")
  AjaxResponse send(@RequestParam("phoneNo") String phoneNo,
                    @RequestParam("content") String content);

}

书写SmsServiceFallback代码,该类要实现FeignClient注解的接口函数。当使用Feign客户端远程调用SmsService .send方法,如果远程服务不可达(网络不可达或宕机),就会快速执行SmsServiceFallback.send方法作为fallback。

@Component
public class SmsServiceFallback implements SmsService {

   @Override
   public AjaxResponse send(String phoneNo, String content) {
      return AjaxResponse.error(CustomExceptionType.SYSTEM_ERROR
                  ,"短信发送接口失败!");
   }
}
二、远程服务调用异常传递的问题

参考《服务熔断降级hystrix》章节中的《远程服务调用异常传递的问题》学习。把文章中的hystrix当成sentinel看,把HystrixCommand注解当成SentinelResource注解看。问题是一样的,原理是一样的。所以此处简写,只写不同的部分。


人为制造一个被除数为0的异常,将会执行SmsControllerHandler 类中的sendFallback方法。

public class SmsControllerHandler {
  public static AjaxResponse sendFallback(@RequestParam String phoneNo,
                                              @RequestParam String content,
                                              Throwable e) {
    return AjaxResponse.error(CustomExceptionType.SYSTEM_ERROR,
            "短信服务发送接口出现错误");
  }
}
三、sentinel-feign异常拦截与处理

通过上面的实现,远程的服务提供者已经可以传递异常了,剩下的工作就是服务调用端如何进行异常的拦截和处理,从而本地服务如果有数据库操作,实现事务回滚。参考《服务熔断降级hystrix》章节中的《Hystrix-Feign异常拦截与处理》学习,此处简写!

3.1.第一种处理方式

最简单的处理方式就是:我们在接收到远程服务的响应结果Response Body(对于我们的项目是AjaxResponse)后,判断其内部的状态信息。如果状态信息是业务失败,throw new 自定义异常抛出,触发数据库回滚!

3.1.暂时没有第二种处理方式

但是非常遗憾的是:Sentinel目前并没有一种Exception,和HystrixBadRequestException一样既可以向外抛出,又不会触发FeignClient的Fallback策略。(或者有我不知道) 无法使用Feign的ErrorDecoder进行全局的集中的异常转换。


动态规则nacos集中存储 一、前言

Sentinel 原生版本的规则管理通过API 将规则推送至客户端并直接更新到内存中,当客户端服务重启时限流规则及降级规则的配置就会消失。对于生产环境而言,这显然不能满足要求!
一般来说,规则的推送有下面三种模式:

推送模式说明优点缺点
原始模式API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource)简单,无任何依赖不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境
Pull 模式扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等简单,无任何依赖;规则持久化不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。
Push 模式扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。规则持久化;一致性;快速需要引入引入第三方依赖,并进行小幅度的sentinel dashboard代码改造

下面我们就实现Push模式,采用远程配置中心的方式,首选阿里系的开源产品nacos!

二、架构

Sentinel DashBoard控制台配置发布并推送到nacos配置中心客户端通过监听事件从配置中心准实时获取流控规则。

目前sentinel的动态规则集中存储到nacos做的还很不完善。截止到2020年5月13日,sentinel只能将“限流规则”发布到nacos配置管理中心。并且web页面很不完整,有些按钮将配置可以发布到nacos,有些按钮不能将配置发布到nacos。所以本节内容暂时先学习一下即可,后续还会有新的进展。虽然官网上说这是生产上的使用方案,但是也只是未来在生产上的使用方案,笔者建议暂时最好不要用于生产!

三、Sentinel DashBoard控制台代码调整

原始的Sentinel DashBoard控制台是不能实现将规则配置推送到nacos配置管理中心的,所以我们需要对源代码进行一定程度的改造。首先我们去github获取源码!

git clone https://github.com/alibaba/Sentinel.git

下载完成后用IDEA打开,或有一定的maven下载过程等待。然后找到sentinel-dashboard子模块。

修改sentinel-dashboard子模块的pom.xml,原来的test去掉:


      com.alibaba.csp
      sentinel-datasource-nacos
      

把sentinel-dashboard子模块src/test下面的包com.alibaba.csp.sentinel.dashboard.rule.nacos拷贝到src/main/java对应的package下面。

并修改NacosConfig 内容为如下代码:

@Configuration
public class NacosConfig {

    //这里是新增的nacos server地址
    @Value("${nacos.address}")
    private String address;

    @Bean
    public Converter, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public ConfigService nacosConfigService() throws Exception {
        //使用nacos server 地址
        Properties properties = new Properties();
        properties.put("serverAddr",address);
        return ConfigFactory.createConfigService(properties);
    }
}

sentinel-dashboard子模块的application.properties 配置引入 Nacos,指向nacos服务:

# nacos的访问地址
nacos.address=192.168.161.6:8848

修改java源代码com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2指定对应的 Bean 开启 Nacos 适配。修改部分如下:

@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher> rulePublisher;

修改sidebar.html页面, 流控规则路由从dashboard.flowV1改成dashboard.flow修改部分如下:

  •   流控规则
  • 四、DashBoard打包部署

    java -Dserver.port=8774 
    -Dcsp.sentinel.heartbeat.client.ip=192.168.161.3 
    -Dproject.name=sentinel-dashboard -jar 
    sentinel-dashboard.jar
    
    五、微服务客户端配置

    pom.xml 引入:

    
        com.alibaba.csp
        sentinel-datasource-nacos
    
    

    配置文件:

    spring:
      application:
        name: aservice-rbac
      cloud:
        sentinel:
          datasource:
            ds1:
              nacos:
                serverAddr: 192.168.161.6:8848   
                dataId: ${spring.application.name}-flow-rule
                groupId: SENTINEL_GROUP
                dataType: json
                ruleType: flow
    

    spring.cloud.sentinel.datasource.ds1.nacos.server-addr,nacos的访问地址spring.cloud.sentinel.datasource.ds1.nacos.dataId,nacos中存储规则的dataId,对于dataId使用了${spring.application.name}变量,这样可以根据应用名来区分不同的规则配置spring.cloud.sentinel.datasource.ds1.nacos.groupIdnacos中存储规则的groupId,分组名称叫什么不重要,重要的是统一、易理解。默认是SENTINEL_GROUP,我们配置的也是默认的spring.cloud.sentinel.datasource.ds1.nacos.rule-type=flow表示该数据源存储的是“限流规则”。

    # nacos有这么多的配置分类,从笔者了解目前只支持flow类型同步到nacos
    private static final String FLOW_RULE_TYPE = "flow";
    private static final String DEGRADE_RULE_TYPE = "degrade";
    private static final String SYSTEM_RULE_TYPE = "system";
    private static final String AUTHORITY_RULE_TYPE = "authority";
    
    六、发布流控配置

    如图所示,界面会多了一个回到单机页面的按钮。目前只有通过这个界面上的绿色边框发布的流控规则,才能同步到nacos。我们使用它在sentinel发布一个流控规则。

    登录 Nacos 后台,配置管理->配置列表:

    点击进入配置详情,配置内容如下:

    [{
    	"app": "aservice-rbac",
    	"clusterConfig": {
    		"fallbackToLocalWhenFail": true,
    		"sampleCount": 10,
    		"strategy": 0,
    		"thresholdType": 0,
    		"windowIntervalMs": 1000
    	},
    	"clusterMode": false,
    	"controlBehavior": 0,
    	"count": 1.0,
    	"gmtCreate": 1589348141737,
    	"gmtModified": 1589348141737,
    	"grade": 1,
    	"id": 10,
    	"ip": "192.168.1.4",
    	"limitApp": "default",
    	"port": 8719,
    	"resource": "sysuserPwdReset",
    	"strategy": 0
    }]
    

    可以看到上面配置规则是一个数组类型,数组中的每个对象是针对每一个保护资源的配置对象,每个对象中的属性解释如下:

    resource:资源名,即限流规则的作用对象limitApp:流控针对的调用来源,若为 default 则不区分调用来源grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制count:限流阈值strategy:调用关系限流策略controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)clusterMode:是否为集群模式


    热点参数限流 一、何为热点参数限流

    何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

    商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

    热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

    Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。

    举一个大家都知道的例子:娱乐圈的顶流鹿晗在新浪微博宣布和关晓彤恋爱,新浪微博几乎瘫痪!如果新浪微博当时有“热点参数限流”的流控机制,这个问题就很好解决,针对“鹿晗”和“关晓彤”配置热点参数限流,就可以解决某个参数流量激增导致的问题,不影响整体业务的运行!

    二、热点参数接口

    我们的代码内有这样一个接口,我们以它为例为大家讲解热点参数限流。这个接口是一个用户所属组织的树形结构查询接口。

    username:必填参数,用户名称orgNameLike:选填参数,组织名称关键字,用于模糊查询orgStatus:选填参数,组织的禁用状态。

    三、配置热点参数限流基础规则

    限流模式只有QPS模式(这才叫热点)。参数索引代表@SentinelResource注解的方法参数,0代表第一个参数,1代表第二个参数。单机阀值以及统计窗口时长表示在此窗口时间超过阀值就限流。


    上面的配置的含义是:针对“/sysodg/tree”资源的第二个参数orgNameLike(参数索引从0开始),1秒钟(统计窗口时长)访问超过1次(单机阈值)就进行限流。

    3.1.快速点击测试(携带orgNameLike参数)
    http://127.0.0.1:8401/sysorg/tree?username=admin&orgNameLike=上海
    

    当我们的请求携带了索引下标为1的参数orgNameLike,快算点击postman,超过一秒一次,得到下面的结果:

    3.2.快速点击测试(不携带orgNameLike参数)
    http://127.0.0.1:8401/sysorg/tree?username=admin
    

    当我们的请求不携带了索引下标为1的热点参数orgNameLike,快算点击postman,超过一秒一次。不论怎么点击都返回正确的组织机构结果。

    四、配置参数例外项目

    如果只是上面的基础配置,还达不到针对特定参数值进行限流的作用。为了达到针对参数特定值进行限流的作用,sentinel的热点参数功能提供了例外项目。

    注意看参数例外项的配置,上图配置的含义就是

    针对“/sysodg/tree”资源的第二个参数orgNameLike(参数索引从0开始),1秒钟(统计窗口时长)访问超过1次(单机阈值)就进行限流。但是当参数orgNameLike的值为北京的时候,限流单机阈值是5。

    就好像所有人在微博的热度限流为每秒1000次,鹿晗流量大给他分配每秒10000流量。这就是极限了,不能再大了!再大系统就扛不住了!就是这个意思!


    系统自适应限流

    如果说我们之前讲的所有限流策略都是进行接口级别的限流,那么系统自适应的限流是从服务运行的整体角度进行应用级别的入口的流量限制。

    一、系统规则

    系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

    系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

    系统规则支持以下的模式:

    Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的maxQps * minRt估算得出。设定参考值一般是CPU cores * 2.5。CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

    注意:以上的系统规则,只有Load负载是自适应的,其他的都是达到阈值后触发系统保护。建议使用Load负载自适应!

    二、如何查看linux系统的运行信息

    我们要进行服务自适应的规则配置阈值,我们在平时服务运行的时候就要知道它的正常值。

    怎么查看服务运行主机的当前Load负载?w命令
    怎么查看服务运行主机的当前CPU使用率?htop命令
    怎么查看当前服务运行的平均RT和QPS?通过sentinel的“实时监控”

    怎么查看当前服务运行的并发线程数量?

    ps -Lf <服务进程ID>|wc -l 
    

    转载请注明:文章转载自 www.mshxw.com
    本文地址:https://www.mshxw.com/it/732029.html
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号