官方文档:https://sentinelguard.io/zh-cn/docs/introduction.html
官方示例:https://github.com/alibaba/Sentinel/tree/master/sentinel-demo
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
https://github.com/alibaba/Sentinel/wiki/Sentinel%E5%B7%A5%E4%BD%9C%E4%B8%BB%E6%B5%81%E7%A8%8B
简单演示,跟多参考官方示例项目
com.alibaba.csp sentinel-core
private static final String RESOURCE_NAME = "hello";// 进行sentinel流控 ,定义资源@RequestMapping(value = "/hello")public String hello() {// 1.sentinel针对资源进行限制的 1.5.0 版本开始可以利用 try-with-resources 特性try(Entry entry = SphU.entry(RESOURCE_NAME)) {// 被保护的业务逻辑String str = "hello world";log.info("====="+str+"=====");return str;} catch (BlockException e1) {// 资源访问阻止,被限流或被降级//进行相应的处理操作log.info("block!");return "被流控了!";}}
根据资源名称,将资源和规则进行绑定
@PostConstruct private static void initFlowRules(){// 流控规则List rules = new ArrayList<>();// 流控FlowRule rule = new FlowRule();// 为哪个资源进行流控rule.setResource(RESOURCE_NAME);// 设置流控规则 QPSrule.setGrade(RuleConstant.FLOW_GRADE_QPS);// 设置资源的限流阈值,一秒只处理一条请求rule.setCount(1);rules.add(rule);// 加载配置好的规则FlowRuleManager.loadRules(rules);}
一秒内只有一条记录成功了,下图中的http1和http2是同一个请求,红色是断言不通过,
基础示例基础上
com.alibaba.csp sentinel-annotation-aspectj
注意:注解方式埋点不支持 private 方法。
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource
注解包含以下属性:
value
:资源名称,必需项(不能为空)
entryType
:entry 类型,可选项(默认为 EntryType.OUT
)
blockHandler
/ blockHandlerClass
: blockHandler
对应处理 BlockException
的函数名称,可选项。blockHandler 函数访问范围需要是 public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException
。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
Throwable
类型的参数用于接收对应的异常。fallbackClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所以类型的异常(除了 exceptionsToIgnore
里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
Throwable
类型的参数用于接收对应的异常。fallbackClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。exceptionsToIgnore
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(
DegradeException
)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException
时只会进入 blockHandler
处理逻辑。若未配置 blockHandler
、fallback
和 defaultFallback
,则被限流降级时会将 BlockException
直接抛出。
注意之前的测试代码进行注释
@Configuration
public class SentinelAspectConfiguration {@Beanpublic SentinelResourceAspect sentinelResourceAspect() {return new SentinelResourceAspect();}
}
private static final String USER_RESOURCE_NAME = "user";@RequestMapping("/user")@SentinelResource(value = USER_RESOURCE_NAME, fallback = "fallbackHandleForGetUser",/*exceptionsToIgnore = {ArithmeticException.class},*//*blockHandlerClass = User.class,*/ blockHandler = "blockHandlerForGetUser")public User getUser(String id) {int a=1/0;return new User("xushu");}public User fallbackHandleForGetUser(String id,Throwable e) {e.printStackTrace();return new User("异常处理");}// BlockException 可以用来确认触发的是哪一种规则public User blockHandlerForGetUser(String id, BlockException ex) {ex.printStackTrace();return new User("流控!!");}
@PostConstructprivate static void initFlowRules(){// 流控规则List rules = new ArrayList<>();// 通过@SentinelResource来定义资源并配置降级和流控的处理方法FlowRule rule2 = new FlowRule();//设置受保护的资源rule2.setResource(USER_RESOURCE_NAME);// 设置流控规则 QPSrule2.setGrade(RuleConstant.FLOW_GRADE_QPS);// 设置受保护的资源阈值rule2.setCount(1);rules.add(rule2);// 加载配置好的规则FlowRuleManager.loadRules(rules);}
1、异常代码部分属于业务逻辑一部分,所以触发流控规则后,只会进入blockHandlerForGetUser ,而不会进入fallbackHandleForGetUser
2、fallback = “fallbackHandleForGetUser”, fallbackClass = ExceptionUtil.class ,两个注解同时使用使用其他类的函数
Sentinel 控制台包含如下功能:
注意:Sentinel 控制台目前仅支持单机部署。Sentinel 控制台项目提供 Sentinel 功能全集示例,不作为开箱即用的生产环境控制台,若希望在生产环境使用请根据文档自行进行定制和改造。
我这里sentinel核心包的版本是1.8.5,这里控制台版本直接下载最新的(也是1.8版本),
下载地址:https://github.com/alibaba/Sentinel/releases
有需要的可以下载源码,修改配置项和代码之后再打包
源码位置:https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard
启动配置项调整(如果需要修改日志配置,应用名称):https://github.com/alibaba/Sentinel/wiki/%E5%90%AF%E5%8A%A8%E9%85%8D%E7%BD%AE%E9%A1%B9
权鉴配置:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0#%E9%89%B4%E6%9D%83
控制台功能介绍:https://github.com/alibaba/Sentinel/blob/master/sentinel-dashboard/Sentinel_Dashboard_Feature.md
权鉴配置的两个配置项
sentinel.dashboard.app.hideAppNoMachineMillis 是否隐藏无健康节点的应用,距离最近一次主机心跳时间的毫秒数,默认关闭
sentinel.dashboard.removeAppNoMachineMillis 是否自动删除无健康节点的应用,距离最近一次其下节点的心跳时间毫秒数,默认关闭
启动命令
java -jar -Dserver.port=8858 -Dsentinel.dashboard.auth.password=123456 -Dsentinel.dashboard.auth.username=sry sentinel-dashboard-1.8.6.jar
nohup java -jar -Dserver.port=8858 -Dsentinel.dashboard.auth.password=123456 -Dsentinel.dashboard.auth.username=sry sentinel-dashboard-1.8.6.jar >/dev/null 2>&1 &
然后就可以了,默认登录进去啥也没有,需要启动连接控制台的客户端
文档地址
com.alibaba.csp sentinel-transport-simple-http
1、java启动参数
# 启动时指定server地址
java -jar -Dcsp.sentinel.dashboard.server=61.171.5.6:8858 demo8-sentinel-base.jar
2、配置文件
客户端启动配置项
用户可以通过 -Dcsp.sentinel.config.file
参数配置 properties 文件的路径,支持 classpath 路径配置(如 classpath:sentinel.properties
)。默认 Sentinel 会尝试从 classpath:sentinel.properties
文件读取配置,读取编码默认为 UTF-8。
配置
sentinel.properties
csp.sentinel.dashboard.server=61.171.5.6:8858
# 如果不指定,启动时注册到sentinel控制台的名称会是springboot启动类的类名
project.name=sentinel-base
需要先访问应用程序相关接口,应用名称才会在sentinel控制台上显示,启动多个实例时,控制台应用名称右侧会显示实例的个数,和正常实例的个数
发现如果控制台和应用不是在同一台机器上,实时监控界面没有数据,有数据的情况如下
处理
网上说的一个是时间一致,然后是单独指定客户端ip
时间一致
ntpdate ntp1.aliyun.com
yum install ntpdate -y
客户端ip指定
csp.sentinel.heartbeat.client.ip=192.168.8.115
由于我本机的内网ip无论怎么指定,天翼云的服务器怎么也访问不到为本地,以上配置对我没有,不过我测试了,控制台和应用不在同一机器,但是只要网络是通的,就不影响实时监控的显示。但是为了后续方便测试,可能我控制台需要部署在本地了
部署在本地后本通过代码设置的流控队则也被加载成功,可以在控制台上进行修改,修改后的规则对于应用实时生效
测试的时候大部分停止的应用,测试的应用在控制台上一直显示很碍眼,控制台启动添加如下参数
# 是否自动删除无健康节点的应用,距离最近一次其下节点的心跳时间毫秒数,默认关闭 最小值120000
sentinel.dashboard.removeAppNoMachineMillis
# 是否隐藏无健康节点的应用,距离最近一次主机心跳时间的毫秒数,默认关闭最小值60000
sentinel.dashboard.app.hideAppNoMachineMillis
# 距离最近心跳时间超过指定时间是否自动删除失联节点,默认关闭 最小值300000
sentinel.dashboard.autoRemoveMachineMillis
nohup java -jar -Dserver.port=8858 -Dsentinel.dashboard.autoRemoveMachineMillis=300000 -Dsentinel.dashboard.auth.password=123456 -Dsentinel.dashboard.auth.username=sry sentinel-dashboard-1.8.6.jar >/dev/null 2>&1 &
然后我发现在机器列表部分移除失联的实例后,刷新页面就可以了。。。。
Sentinel控制台与微服务端之间,实现了一套服务发现机制,集成了Sentinel的微服务都会将元数据传递给Sentinel控制台,架构图如下所示:
流控针对privoder 熔断降级 针对consumer
sentinel还有很多种使用场景,你可以在dubbo官网找到dubbo结合sentinel使用的示例。就springcloudalibaba官方提供的示例而言,有整合openfeign,webflux,gateway等的示例。
还有一种不依赖控制台,通过json配置文件来定义规则细则,springboot的配置文件中通过配置指定资源和json文件中定义规则的关联关系。
主要是一个可以提供web访问的项目,引入依赖spring-cloud-starter-alibaba-sentinel,这个依赖包含了上文中sentinel核心、注解支持,控制台支持等相关依赖。
org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-sentinel
spring:cloud:sentinel:transport:# 控制台地址dashboard: 127.0.0.1:8858# 指定sentinel控制台获取应用数据的端口,被占用会端口号默认 +1port: 8719
正常的写一个可以访问的controller即可,不需要加注解和任何规则,然后访问接口以便让sentinel控制台显示这个应用
基于代码的资源定义和规则定义上面的基础示例和注解方式示例中已经讲过了,而且本地定义的规则在连接上控制台后会被控制台进行加载
这里主要基于springboot整合sentinel的示例来演示sentinel相关的规则配置。
会显示当前被访问接口实时的QPS,拒绝的请求数,通过的请求数,接口一段时间未访问后,会从实时监控界面消失,界面会自动更新数据。
1、你发起对应用接口的请求后(没有访问过的接口不显示),接口的路径会作为资源名显示在这里,需要说明的是这个路径是不包含server.servlet.context-path指定的根路径
2、可以针对资源名进行相关的规则配置,而且一般是在这里进行规则配置,其他页面应该是用于查看
3、如果你需要指定service层面的资源,还是需需要使用@SentinelResource注解,当然和接口一样,需要调用后才能在控制台显示
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。 ==== FlowRule RT(响应时间) 1/0.2s =5
同一个资源可以创建多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果。
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS 模式 |
limitApp | 流控针对的调用来源 | default,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流 | 直接拒绝 |
clusterMode | 是否集群限流 | 否 |
参考文档: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS。类型由 FlowRule 的 grade 字段来定义。其中,0 代表根据并发数量来限流,1 代表根据 QPS 来进行流量控制。
QPS(Query Per Second):每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。
一秒中允许多少个请求
由多少个线程去处理请求,假如限制了这个资源只能由一个线程去处理请求,那么当这个请求没有结束时,其他请求拒绝,和时间没关系。
默认就是直接,就是我配置的限流阈值就是针对当前资源,触发后也是当前资源被限制
按照图片中的示例,就是关联资源getUser触发限流阈值后(图片里的阈值就是关联资源的阈值,而不是当前资源的阈值),当前的资源会被限制访问
测试结果
boot1是 /basic/hello这个资源,boot是getUser这个资源
/hello和 /basic/hello 两个资源都访问getUser,他们被称为getUser的入口资源,下图配置中表示当前资源到达阈值后,限制其中一个入口资源的访问
测试会发现链路规则不生效
注意,高版本此功能直接使用不生效,如何解决?
从1.6.3版本开始,Sentinel Web filter默认收敛所有URL的入口context,导致链路限流不生效。
从1.7.0版本开始,官方在CommonFilter引入了WEB_CONTEXT_UNIFY参数,用于控制是否收敛context,将其配置为false即可根据不同的URL进行链路限流。
1.8.0 需要引入sentinel-web-servlet依赖
com.alibaba.csp sentinel-web-servlet
添加配置类,配置CommonFilter过滤器,指定WEB_CONTEXT_UNIFY=false,禁止收敛URL的入口context
@Configuration
public class SentinelConfig {@Beanpublic FilterRegistrationBean sentinelFilterRegistration() {FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(new CommonFilter());registration.addUrlPatterns("/*");// 入口资源关闭聚合 解决流控链路不生效的问题registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");registration.setName("sentinelFilter");registration.setOrder(1);return registration;}
}
配置后控制台链路结构
"/>
测试结果
当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:快速失败(直接拒绝)、Warm Up(预热)、匀速排队(排队等待)。对应 FlowRule 中的 controlBehavior 字段。
(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
冷加载因子: codeFactor 默认是3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
测试如图,可以看到,通过的请求缓慢上升,达到12,大致是这样
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
该方式的作用如下图所示:
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
如图,qps是5,加入过来了10个请求,那么多余的5个在超时时间内会处于等待状态,直到前五个请求有处理完成的,或者超过了超时时间直接拒绝,测试就不测了
官方文档 :https://sentinelguard.io/zh-cn/docs/cluster-flow-control.html
之后再说吧,似乎比较麻烦,还有部署一个服务端
Sentinel 集群流控支持限流规则和热点规则两种规则,并支持两种形式的阈值计算方式:
如果一个应用有多个实例,配置的规则根据ip:端口进行区分属于哪个实例
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
官方文档:https://sentinelguard.io/zh-cn/docs/circuit-breaking.html
熔断降级规则说明
熔断降级规则(DegradeRule)包含下面几个重要的属性:
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 引入) |
慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
这里是最新版本,之前的版本应该没有统计时长吧
下面的配置大致是,在统计时长内,至少有10个请求过来(没有达到这个请求数不会触发),其中按照比例阈值,有3个请求超过了最大的RT,那么这个接口熔断20秒,20内的返回看设定的统一的BlockException处理,或者指定的降级方法
测试话,可以打断点,或者thread.sleep,方便测试可以统计时长可以调长一点
测试结果如下,除了打断点的那一个,其他的请求都被降级处理了,然后确实要20秒才能再次访问,且访问的第一个请求响应时长不能大于最大RT,否则马上触发再次熔断
异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
测试代码如下,
@RequestMapping("/test2")
public String test2() {atomicInteger.getAndIncrement();if (atomicInteger.get() % 2 == 0){//模拟异常和异常比率int i = 1/0;}return "========test2()========";
}
异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。
官方文档:https://sentinelguard.io/zh-cn/docs/parameter-flow-control.html
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
注意:
测试
@RequestMapping("/get/{id}")@SentinelResource(value = "getById"/*,blockHandler = "HotBlockHandler"*/)public String getById(@PathVariable("id") Integer id) {System.out.println("正常访问");return "正常访问";}
资源注册效果如图
下图中资源名应该配置@SentinelResource中的资源名,否则无效,我就不重新截图了
测试结果如下,对于例外参数3,前十次是请求成功的,相当于就是一个流控规则,但是对于特定的参数可以设置例外的阈值,但是抛出的异常似乎是 com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException,所以建议在注解中指定回调方法,和异常处理方法
官方文档
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
黑白名单
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
调用方信息通过
ContextUtil.enter(resourceName, origin)
方法中的origin
参数传入。
官方示例,似乎将 origin 作为参数传入,来判断资源方
private static void testFor(/*@NonNull*/ String resource, /*@NonNull*/ String origin) {ContextUtil.enter(resource, origin);Entry entry = null;try {entry = SphU.entry(resource);System.out.println(String.format("Passed for resource %s, origin is %s", resource, origin));} catch (BlockException ex) {System.err.println(String.format("Blocked for resource %s, origin is %s", resource, origin));} finally {if (entry != null) {entry.exit();}ContextUtil.exit();}}
网上找到了这个,你可以拿到http请求对象,然后获取对象中的参数,或者ip等作为控制的应用名
注意包名:com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser
@Component
public class RequestOriginParserImpl implements RequestOriginParser {@Overridepublic String parseOrigin(HttpServletRequest request) {return request.getRemoteAddr();}
}
主要是调用服务端服务失败后有兜底
需要nacos和openfeign相关依赖
org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign org.springframework.cloud spring-cloud-loadbalancer com.alibaba.cloud spring-cloud-starter-alibaba-sentinel
# nacos服务端配置
spring:cloud:nacos:server-addr: 61.171.5.6:30848discovery:username: nacospassword: nacossentinel:transport:port: 8719dashboard: 127.0.0.1:8858
# 主要是这行配置
feign:sentinel:# openfeign整合sentinelenabled: true
注意注解中增加的fallback
@FeignClient(name = "nacos-producer", path = "/nacos-producer", fallback = DemoServiceImplFallBack.class)
public interface DemoService {@RequestMapping(method = RequestMethod.GET, value = "/stores/{storeId}")Store getStores(@PathVariable("storeId") Long storeId);
}
@Component
public class DemoServiceImplFallBack implements DemoService{@Overridepublic Store getStores(Long storeId) {return new Store();}
}
感觉fallback是@FeignClient的一个属性,似乎和sentinel无关,但是测试了一下,不添加的结果如下,如果需要回调的类生效,还是要添加sentinel和fallback的依赖
控制台资源名如图,配置流控规则测试正常
springwebmvc接口资源限流入口在HandlerInterceptor的实现类AbstractSentinelInterceptor的preHandle方法中,对异常的处理是BlockExceptionHandler的实现类
sentinel 1.7.1 引入了sentinel-spring-webmvc-adapter.jar
BlockExceptionHandler 的实现类统一处理BlockException,其有一个默认实现DefaultBlockExceptionHandler,其返回Blocked by Sentinel (flow limiting)
如果要自定义实现,你只需要实现这个处理器即可,不需要添加额外的配置
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {Logger log= LoggerFactory.getLogger(this.getClass());@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {// getRule() 资源 规则的详细信息log.info("BlockExceptionHandler BlockException================"+e.getRule());Result r = null;if (e instanceof FlowException) {r = Result.error(100,"接口限流了");} else if (e instanceof DegradeException) {r = Result.error(101,"服务降级了");} else if (e instanceof ParamFlowException) {r = Result.error(102,"热点参数限流了");} else if (e instanceof SystemBlockException) {r = Result.error(103,"触发系统保护规则了");} else if (e instanceof AuthorityException) {r = Result.error(104,"授权规则不通过");}//返回json数据response.setStatus(500);response.setCharacterEncoding("utf-8");response.setContentType(MediaType.APPLICATION_JSON_VALUE);new ObjectMapper().writeValue(response.getWriter(), r);}
}
需要说明的是@SentinelResource(value=“getUser”,blockHandler = “blockHandlerGetUser”)中指定的回调方法优先级更高
官方文档:https://sentinelguard.io/zh-cn/docs/dynamic-rule-configuration.html
https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel
控制台的规则在控制台关闭后就消失了,或者应用关闭后也会消失,当然在应用启动时,控制台配置的规则不会因为控制台的关闭而失效
Sentinel规则的推送有下面三种模式:
推送模式 | 说明 | 优点 | 缺点 |
---|---|---|---|
原始模式 | API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource) | 简单,无任何依赖 | 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境 |
Pull 模式 | 扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 | 简单,无任何依赖;规则持久化 | 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。 |
Push 模式 | 扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 | 规则持久化;一致性;快速 | 引入第三方依赖 |
如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中:
这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。
pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:
官方文档:https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel
com.alibaba.csp sentinel-datasource-nacos
如果nacos有密码,注意添加密码配置,
spring:cloud:sentinel:transport:dashboard: 127.0.0.1:8858# 指定sentinel控制台获取应用数据的端口,被占用会端口号默认 +1,设置后还是会自动+1port: 8719
# web-context-unify: false # 默认将调用链路收敛, 导致链路流控效果无效datasource:ds1: # 可以配置多个,初始化不同的规则nacos:server-addr: 61.171.5.6:30848dataId: ${spring.application.name}groupId: DEFAULT_GROUPdata-type: jsonrule-type: flow # RuleType枚举类username: nacospassword: nacos#namespace: 6b0dd542-fa60-48ef-a79f-14710581b660
具体的属性参考对应的规则类,和对照控制台
[{"resource": "/hello","controlBehavior": 0,"count": 10.0,"grade": 1,"limitApp": "default","strategy": 0}
]
启动成功后如果发现规则没有生效,比如你通过endpoint或者控制台确定规则没生效后,可以去查看sentinel日志,也可以先确定nacos上的配置是否有监听者,这里不多说,成功贴图
从 Sentinel 1.4.0 开始,Sentinel 控制台提供 DynamicRulePublisher
和 DynamicRuleProvider
接口用于实现应用维度的规则推送和拉取,并提供了相关的示例。Sentinel 提供应用维度规则推送的示例页面(/v2/flow
),用户改造控制台对接配置中心后可直接通过 v2 页面推送规则至配置中心。改造详情可参考 应用维度规则推送示例。
总之就是要我们自己进行改造,但是官方提供了示例,所以你需要下载控制台源代码,不过github上已经有改造好的,
可以借鉴:https://github.com/eacdy/Sentinel-Dashboard-Nacos
似乎是这一个,测试我就不测试了
这个主要是看官方的示例项目中有这样的配置,记录一下
ds1是数据源的意思,名称随意,
spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json
spring.cloud.sentinel.datasource.ds1.file.data-type=json
spring.cloud.sentinel.datasource.ds1.file.rule-type=flowspring.cloud.sentinel.datasource.ds2.file.file=classpath: degraderule.json
spring.cloud.sentinel.datasource.ds2.file.data-type=json
spring.cloud.sentinel.datasource.ds2.file.rule-type=degrade
不一定要json格式,可以是xml格式
flowrule.json
[{"resource": "/hello","controlBehavior": 0,"count": 1,"grade": 1,"limitApp": "default","strategy": 0},{"resource": "/test","controlBehavior": 0,"count": 0,"grade": 1,"limitApp": "default","strategy": 0},{"resource": "GET:http://www.taobao.com","controlBehavior": 0,"count": 0,"grade": 1,"limitApp": "default","strategy": 0}
]
需要引入依赖加配置,再说
management:endpoints:web:exposure:include: '*'
org.springframework.boot spring-boot-starter-actuator
访问http://localhost:8800/actuator/sentinel, 可以查看flowRules
示例中看到了这个,使用的话待定,仅做记录
@Bean
@SentinelRestTemplate(blockHandler = "handleException",blockHandlerClass = ExceptionUtil.class)
public RestTemplate restTemplate() {return new RestTemplate();
}
public final class ExceptionUtil {private ExceptionUtil() {}public static SentinelClientHttpResponse handleException(HttpRequest request,byte[] body, ClientHttpRequestExecution execution, BlockException ex) {System.out.println("Oops: " + ex.getClass().getCanonicalName());return new SentinelClientHttpResponse("custom block info");}}
resttemplate:sentinel: #@SentinelRestTemplate 开启、关闭enabled: true
测试代码
@GetMapping("/testSentinelRestTemplate/{userId}")public String testSentinelRestTemplate(@PathVariable Integer userId){UserDTO userDTO = restTemplate.getForObject("http://user-center/users/{userId}",UserDTO.class,userId);return userDTO.toString();}