阿里限流神器Sentinel夺命连环 17 问?(下篇)
11、如何自定义限流返回的异常信息?
在前面的例子中,无论是熔断降级还是被限流返回的异常信息都是Blocked by Sentinel (flow limiting)
,这个是Sentinel默认的异常信息。
很显然默认的异常信息并不能满足我们的业务需求,因此我们需要根据前后端规则制定自己的异常返回信息。
这里将会用到一个注解@SentinelResource
,这个在上文也是提到过,这个注解中有两个关于限流兜底方法的属性,如下:
- blockHandler:对应处理
BlockException
的函数名称。blockHandler 函数访问范围需要是 public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException
。blockHandler 函数默认需要和原方法在同一个类中。 - blockHandlerClass:指定
blockHandlerClass
为对应的类的 Class
对象,注意对应的函数必需为 static
函数,否则无法解析。
官方文档:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
使用@SentinelResource
注解自定义一个限流异常返回信息,先自定义一个资源,指定兜底方法为handler
,代码如下:
第二步:写个对应得兜底方法,必须在同一个类中,代码如下:
第三步:对资源QueryOrder
新增一个限流规则,如下图:
第四步:写个controller,代码就不晒了,自己写吧,哈哈。。。。
第五步:调用接口,疯狂点击,将会出现兜底方法中定义的返回信息,如下图:
到这儿基本算是成功了,但是有个问题:兜底方法必须要和业务方法放在同一个类中,这样代码耦合度不是很高吗?
@SentinelResource
提供一个属性blockHandlerClass
,完美的解决了这一个问题,能够将兜底方法单独放在一个类中,下面来介绍一下。
第一步:新建一个单独的类CommonHandler
来放置兜底方法,代码如下:
第二步:在@SentinelResource
注解中指定blockHandlerClass为上面的类,blockHandler指定兜底方法名,代码如下:
好了,至此就完成了,自己照着试试吧.......
上述源码在sentinel-openfeign-provider9009这个模块中,源码获取方式见文末。
12、如何对异常进行降级处理?
程序员每天都在制造BUG,没有完美的代码,也没有完美的程序员,针对代码的运行时异常我们无法避免,但是我们可以当出现异常的时候进行捕获并做出相应的处理,我们称之为降级处理。
异常的降级还是要用到@SentinelResource
注解,其中相关的几个属性如下:
- fallback:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了
exceptionsToIgnore
里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求: - 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要和原函数一致,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常。 - fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的 Class
对象 - 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.8.0 版本开始,defaultFallback
支持在类级别进行配置。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(
DegradeException
)进行处理,不能针对业务异常进行处理。官方文档:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
下面定义一个创建订单的接口,手动制造一个1/0
异常,代码如下:
上述接口并没有进行异常降级处理,因此调用该接口直接返回了异常信息,非常不友好,如下图:
我们可以使用fallback
指定异常降级的兜底方法,此时业务方法改造如下:
使用fallbackClass
属性指定单独一个类处理异常降级,降低了代码的耦合度,fallback
属性指定了降级兜底的方法,代码如下:
此时再次访问接口,虽然有异常,但是返回的确实降级兜底方法中的返回信息,如下图:
到了这里基本满足了异常降级的处理需求,但是仍然有个疑问:能否只用一个方法处理全部的异常?
答案是:能,必须能,此时就要用到defaultFallback
这个属性了,指定默认的降级兜底方法,此时的业务方法变成如下代码:
defaultFallback
属性指定了默认的降级兜底方法,这个方法代码如下:
好了,异常降级处理到这儿已经介绍完了,但是仍然有一个问题:若 blockHandler 和 fallback 都进行了配置,那么哪个会生效?
结论:若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出
BlockException
时只会进入 blockHandler
处理逻辑。若未配置 blockHandler
、fallback
和 defaultFallback
,则被限流降级时会将 BlockException
直接抛出。
将createOrder
这个业务接口改造一下,同时指定blockHandler和fallback,代码如下:
此时不配置任何规则,直接访问接口,可以看到这里直接进入了异常降级处理,如下图:
我们对createOrder
这个资源配置降级规则:60秒内如果出现2个以上的异常直接限流,如下图:
此时我们再次访问这个接口,可以看到前两次直接进入了fallback
指定的方法中(并未达到限流的异常数阈值),两次之后就被限流了,进入了blockHandler
方法中,效果如下图:
上述源码在sentinel-openfeign-provider9009这个模块中,源码获取方式见文末。
13、sentinel的黑白名单如何设置?
顾名思义,黑名单就是拉黑呗,拉黑就是不能访问了呗,sentinel能够针对请求来源进行是否放行,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
sentinel控制台对应得规则配置如下图:
该规则对应得源码为com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule
,几个属性如下:
-
resource
:资源名,即限流规则的作用对象。 -
limitApp
:对应的黑名单/白名单,不同 origin 用 ,
分隔,如 appA,appB
。 -
strategy
:限制模式,AUTHORITY_WHITE
为白名单模式,AUTHORITY_BLACK
为黑名单模式,默认为白名单模式。
官方文档:https://github.com/alibaba/Sentinel/wiki/%E9%BB%91%E7%99%BD%E5%90%8D%E5%8D%95%E6%8E%A7%E5%88%B6
这里有个问题:请求来源是什么,怎么获取?
Sentinel提供了一个接口RequestOriginParser
,我们可以实现这个接口根据自己业务的规则解析出请求来源名称。
下面我以IP作为区分请求来源,代码如下:
然后将127.0.0.1
设置为黑名单,如下图:
直接访问:http://127.0.0.1:9009/sentinel/rate/order/query?id=1002,结果如下图:
可以看到被限流了哦.................
好了,黑白名单就介绍到这里。
上述源码在sentinel-openfeign-provider9009这个模块中,源码获取方式见文末。
14、限流规则如何持久化?
Sentinel默认限流规则是存储在内存中,只要服务重启之后对应得限流规则也会消失,实际的生产中肯定是不允许这种操作,因此限流规则的持久化迫在眉睫。
sentinel官方文档提供了两种持久化模式,分别如下:
但是官方推荐使用Push
模式,下面陈某就Push模式介绍一下持久化限流规则。这里使用Nacos作为配置中心。
盗用官方一张架构图,如下:
1、添加依赖
这里需要添加一个依赖,如下:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2、配置文件中配置相关信息
既然使用到了Nacos作为配置中心,肯定是要配置相关的地址、dataId...
在application.yml
配置文件中添加如下配置:
spring:
cloud:
sentinel:
## nacos持久化配置
datasource:
## 配置流控规则,名字任意
ds-flow:
nacos:
## nacos的地址
server-addr: 127.0.0.1:8848
## 配置ID
dataId: ${spring.application.name}-flow
## 配置分组,默认是DEFAULT_GROUP
groupId: DEFAULT_GROUP
## 配置存储的格式
data-type: json
## rule-type设置对应得规则类型,总共七大类型,在com.alibaba.cloud.sentinel.datasource.RuleType这个枚举类中有体现
rule-type: flow
## 配置降级规则,名字任意
ds-degrade:
nacos:
## nacos的地址
server-addr: 127.0.0.1:8848
## 配置ID
dataId: ${spring.application.name}-degrade
## 配置分组,默认是DEFAULT_GROUP
groupId: DEFAULT_GROUP
## 配置存储的格式
data-type: json
## rule-type设置对应得规则类型,总共七大类型,在com.alibaba.cloud.sentinel.datasource.RuleType这个枚举类中有体现
rule-type: degrade
上述配置仅仅展示了和持久化相关的一些配置,其他相关的配置代码就不贴了,稍后自己看源码。
spring.cloud.sentinel.datasource
下可以配置多个规则,陈某这里只配置了限流和降级规则,其他规则自己尝试配一下,不同规则通过rule-type
区分,其取值都在com.alibaba.cloud.sentinel.datasource.RuleType
这个枚举类中,对应着sentinel中的几大统计规则。
3、在Nacos添加对应的规则配置
上述配置中对应的限流(flow)规则如下图:
上述配置中对应的降级(degrade)规则如下图:
先不纠结JSON数据里面到底是什么,先看效果,全部发布之后,Nacos中总共有了两个配置,如下图:
上图中可以看到我们的两种规则已经在Nacos配置好了,来看一下sentinel中是否已经生效了,如下图:
哦了,已经生效了,由于是push模式,只要nacos中点击发布配置,相关规则配置就会推送到sentinel中。
上述源码在sentinel-openfeign-provider9009这个模块中,源码获取方式见文末。
伏笔:push模式只能保证Nacos中的修改推送到sentinel控制台,**但是sentinel控制台的限流规则修改如何推送到Nacos呢?**别着急,下面将会介绍..............
4、JSON中到底怎么写?
很多人好奇JOSN中的配置到底怎么写?其实很简单,陈某在介绍各种规则的时候都明确告诉你每种规则对应源码中的实现类,比如流控规则对应的类就是com.alibaba.csp.sentinel.slots.block.flow.FlowRule
,JOSN中各个属性也是来源于这个类。
下面陈某列出各个规则的JSON配置,开发中照着改即可。
1、流控规则
[
{
// 资源名
"resource": "/test",
// 针对来源,若为 default 则不区分调用来源
"limitApp": "default",
// 限流阈值类型(1:QPS;0:并发线程数)
"grade": 1,
// 阈值
"count": 1,
// 是否是集群模式
"clusterMode": false,
// 流控效果(0:快速失败;1:Warm Up(预热模式);2:排队等待)
"controlBehavior": 0,
// 流控模式(0:直接;1:关联;2:链路)
"strategy": 0,
// 预热时间(秒,预热模式需要此参数)
"warmUpPeriodSec": 10,
// 超时时间(排队等待模式需要此参数)
"maxQueueingTimeMs": 500,
// 关联资源、入口资源(关联、链路模式)
"refResource": "rrr"
}
]
2、降级规则
[
{
// 资源名
"resource": "/test1",
"limitApp": "default",
// 熔断策略(0:慢调用比例,1:异常比率,2:异常计数)
"grade": 0,
// 最大RT、比例阈值、异常数
"count": 200,
// 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
"slowRatioThreshold": 0.2,
// 最小请求数
"minRequestAmount": 5,
// 当单位统计时长(类中默认1000)
"statIntervalMs": 1000,
// 熔断时长
"timeWindow": 10
}
]
3、热点规则
[
{
// 资源名
"resource": "/test1",
// 限流模式(QPS 模式,不可更改)
"grade": 1,
// 参数索引
"paramIdx": 0,
// 单机阈值
"count": 13,
// 统计窗口时长
"durationInSec": 6,
// 是否集群 默认false
"clusterMode": 默认false,
//
"burstCount": 0,
// 集群模式配置
"clusterConfig": {
//
"fallbackToLocalWhenFail": true,
//
"flowId": 2,
//
"sampleCount": 10,
//
"thresholdType": 0,
//
"windowIntervalMs": 1000
},
// 流控效果(支持快速失败和匀速排队模式)
"controlBehavior": 0,
//
"limitApp": "default",
//
"maxQueueingTimeMs": 0,
// 高级选项
"paramFlowItemList": [
{
// 参数类型
"classType": "int",
// 限流阈值
"count": 222,
// 参数值
"object": "2"
}
]
}
]
4、系统规则
负值表示没有阈值检查。不需要删除参数
[
{
// RT
"avgRt": 1,
// CPU 使用率
"highestCpuUsage": -1,
// LOAD
"highestSystemLoad": -1,
// 线程数
"maxThread": -1,
// 入口 QPS
"qps": -1
}
]
5、授权规则
[
{
// 资源名
"resource": "sentinel_spring_web_context",
// 流控应用
"limitApp": "/test",
// 授权类型(0代表白名单;1代表黑名单。)
"strategy": 0
}
]
注意:对于上述JOSN中的一些可选属性不需要的时候可以删除。
15、限流规则如何推送到Nacos进行持久化?
sentinel默认的持久化只能从nacos推送到sentinel控制台,但是实际生产中肯定是双向修改都能推送的,这个如何解决呢?
其实sentinel官方文档就有说到解决方法,不过需要自己修改sentinel控制台的源码来实现。
这个还是比较复杂的,sentinel只帮我们实现了流控规则的demo,其他的还是要自己修改,这点不太人性化....
在这之前需要自己下载对应版本的sentinel控制台的源码,地址:https://github.com/alibaba/Sentinel/tags
流控规则源码修改
在源码的test目录下有sentinel提供的demo,分别有apollo、nacos、zookeeper,如下图:
这里我们是Nacos,因此只需要nacos包下面的demo。修改步骤如下:
1、去掉sentinel-datasource-nacos依赖的scop
这个sentinel-datasource-nacos依赖默认是<scope>test</scope>
,因此我们需要去掉这个,如下:
<!-- for Nacos rule publisher sample -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
如果你集成的zookeeper或者apollo,则把相应的依赖也要修改。
2、复制test环境下的nacos整个包到main下
将这个nacos包复制到com.alibaba.csp.sentinel.dashboard.rule
这个包下,如下图:
3、将FlowControllerV2中的代码复制到FlowControllerV1中
com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2
这个是sentinel提供的demo,只需要将其中的代码全部覆盖到com.alibaba.csp.sentinel.dashboard.controller.FlowControllerV1
中。
4、修改FlowControllerV1中的代码
直接覆盖掉当然不行,还要做一些修改,如下:
- 修改RequestMapping中的请求url为
/v1/flow
- 修改
ruleProvider
、rulePublisher
的依赖,修改后的代码如下:
@Autowired
//使用nacos的依赖
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
//使用nacos的依赖
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
5、注意nacos的相关配置
com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil
这个工具类中对应的是限流规则在nacos中的一些配置项,有groupId
、dataId
...对应的配置如下:
需要两边统一,可以自己修改。
com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfig
这个类中有个方法如下图:
默认指定的nacos地址是本地的,这个需要修改。
6、完成
以上步骤已经改造了sentinel控制台的流控规则,打包启动控制台代码,命令如下:
mvn clean install -DskipTests=true -pl sentinel-dashboard -am
启动后在控制台添加流控规则,可以看到也会同步推送到nacos,包括增删改。
其他规则修改也很简单,照葫芦画瓢,这里就不再详细说了,后面会单独出一篇文章详细说一下。
16、集群流控如何做?
首先一个简单的问题:为什么需要集群流控?单机流控不香吗?原因如下:
- 对于微服务要想保证高可用,必须是集群,假设有100个集群,那么想要设置流控规则,是不是每个微服务都要设置一遍?维护成本太高了
- 单体流控还会造成流量不均匀的问题,出现总流控阈值没有达到某些微服务已经被限流了,这个是非常糟糕的问题,因此实际生产中对于集群不推荐单体流控。
那么如何解决上述的问题呢?sentinel为我们提供了集群流控的规则。思想很简单就是提供一个专门的server来统计调用的总量,其他的实例都与server保持通信。
集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
集群流控中共有两种身份:
- Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
- Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
sentinel的集群限流有两种模式,分别如下:
- 独立模式(Alone):即作为独立的 token server 进程启动,独立部署,隔离性好,但是需要额外的部署操作。独立模式适合作为 Global Rate Limiter 给集群提供流控服务。
- 嵌入模式(Embedded):即作为内置的 token server 与服务在同一进程中启动。在此模式下,集群中各个实例都是对等的,token server 和 client 可以随时进行转变,因此无需单独部署,灵活性比较好。但是隔离性不佳,需要限制 token server 的总 QPS,防止影响应用本身。嵌入模式适合某个应用集群内部的流控。
下面就以嵌入模式为例介绍一下如何配置。
就以sentinel-openfeign-provider9009
这个模块作为演示,直接启动三个集群,端口分别为9009
、9011
、9013
,如下图:
启动成功,在sentinel控制台将会看到有三个实例已经被监控了,如下图:
此时只需要在控制台指定一个服务为token server,其他的为token client,集群流控->新增token server,操作如下图:
选取一个作为服务端,另外两个作为客户端,此时就已经配置好了,如下图:
此时就可以添加集群流控规则了,可以在sentinel控制台直接添加,也可以通过Nacos直接配置,下图是通过Nacos配置的,如下图:
Nacos推送成功后将会在sentinel控制台看到这条流控规则的配置,如下图:
OK,至此集群流控到这儿就介绍完了,配置好之后可以自己试一下效果,陈某就不再演示了。
官方文档:https://github.com/alibaba/Sentinel/wiki/%E9%9B%86%E7%BE%A4%E6%B5%81%E6%8E%A7
17、网关限流如何配置?
这一块内容在后续介绍到网关的时候会详细讲,这里就不再细说了,有想要了解的可以看官方文档。
官方文档:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81
18、整合openFeign如何实现熔断降级?
这个在上篇openFeign的文章中有详细介绍:openFeign夺命连环9问,这谁受得了?陈某这里就不再重复介绍了,有不知道的可以看上面这篇文章
文章转载自公众号: 码猿技术专栏