在上一篇讲了sentinel的介绍,安装Sentinel控制台、初始化演示工以及程流控规则
接下来学习熔断规则,热点key限流,系统规则,@SentinelResource注解,服务熔断功能以及规则持久化。
熔断规则
官方介绍
-
基本介绍
熔断策略
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 限流降级本身的异常(
BlockException
)不生效。 -
RT(平均响应时间,秒级)
平均响应时间 超出阈值 且 在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
窗口期过后关闭断路器
RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
熔断降级规则(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 引入) |
Sentinel的断路器是
没有半开状态
的
半开的状态系统自动去检测是否请求有异常,
没有异常就关闭断路器恢复使用,
有异常则继续打开断路器不可用。具体可以参考Hystrix
(1)慢调用比例
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 添加一个测试接口,调用接口时休眠2秒,使得此接口的响应时间为1秒以上
@GetMapping("/testD")
public String testD()
{
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
log.info("testD 测试调用比例");
return "------testD";
}
-
控制台添加测试接口熔断规则
-
最大 RT
:慢调用临界 RT,超出该值计为慢调用。单位毫秒 -
比例阈值
:RT模式下慢速请求比率的阈值。默认1.0d -
熔断时长
:断路器打开时的恢复超时(以秒为单位)。超时后,断路器将转换为半开状态以尝试一些请求。单位秒,图中表示触发熔断后,接下来的10秒内请求会自动被熔断,经过10S后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。 -
最小请求数
:可以触发熔断中断的最小请求数(在有效的统计时间范围内)。默认值为5 -
StatIntervalMs
: 统计时长(单位为 ms),如 601000 代表分钟级(1.8.0 引入),默认1000,在控制台没有选项,需代码实现。
使用
jemeter
测试
使用压测工具访问测试接口,查看限流效果图,具体限流过程大概为:
(1)请求进入后台,sentinel会根据设置的统计时长(默认1S)统计时间段内请求的总数
(2)首先判断统计的请求总数是否小于用户的设置的最小请求数(默认5),小于则不熔断,反之则进入下一步
(3)然后根据用户设置的最大 RT,判断统计中的请求是否为慢调用,大于设置值为是慢调用请求
(4)再次计算慢调用请求/总统计请求比例,是否超过设置的比例阈值。
(5)当统计时间内的请求数及慢调用比例阈值都超过设置的阈值后,接下来的熔断时长内请求会自动被熔断
(6)熔断时长结束后,熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
————————————————
版权声明:本文为CSDN博主「云烟成雨csdn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43437874/article/details/111475762
结论:
永远一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒处理完本次任务,
如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了
后续我停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复OK
(2)异常比例
异常比例 (ERROR_RATIO)
:当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% – 100%。
- 在controller添加以下方法
@GetMapping("/testE")
public String testE()
{
log.info("testE 测试异常比例");
int age = 10/0;
return "------testE";
}
-
控制台添加测试接口熔断规则
访问
http://127.0.0.1:8401/testE
直接报错了,直接再刷新。
因为多次调用
达到我们的配置熔断降级条件了,断路器开启(保险丝跳闸)
,微服务不可用了,不再报错error而是服务降级了。
(3)异常数
异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
测试
还是刚刚那个
testE
路径
-
控制台修改测试接口熔断规则
访问
http://127.0.0.1:8401/testE
5s内,先访问5次,当访问了第五次了,在访问一次,也就是第六次。
因为我们配置的统计时长是
5s
,当5s内异常的次数达到了
5次
,第
6次
就进行熔断降级,降级持续
2s
钟。
热点key限流
何为热点?
热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作
官方说明
- 在controller添加以下方法
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2){
return "------testHotKey";
}
public String dealHandler_testHotKey(String p1,String p2,BlockException exception)
{
return "-----dealHandler_testHotKey";
}
sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
-
配置说明
正常访问
http://127.0.0.1:8401/testHotKey
访问
http://127.0.0.1:8401/testHotKey?p1=1
,并不断刷新
访问
http://127.0.0.1:8401/testHotKey?p2=1
,并不断刷新
访问
http://127.0.0.1:8401/testHotKey?p2=1&p1=1
,并不断刷新
结论,只要带有
p1
这个参数并且QPS超过每秒1次都要被降级处理。
@SentinelResource(value = "testHotKey")//异常打到了前台用户界面看到,不友好
//方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")//用了我们自己定义的
上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流,但是我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样,例如:假如当p1的值等于5时,它的阈值可以达到200
-
修改配置
配置说明:当p1等于5的时候,阈值变为200,当p1不等于5的时候,阈值就是平常的1。
注意:热点参数的注意点,参数必须是基本类型或者String
其他:
@SentinelResource
处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;
RuntimeException
int age = 10/0,这个是java运行时报出的运行时异常RunTimeException,@SentinelResource不管
总结
@SentinelResource主管配置出错,运行出错该走异常走异常
系统规则
系统保护规则是从应用级别的
入口流量
进行控制,从单台机器的
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 达到阈值即触发系统保护。
官方文档
@SentinelResource的使用
1.按资源名称限流以及后续处理
-
添加新的
RateLimitController
@RestController
public class RateLimitController
{
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public CommonResult byResource()
{
return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
}
public CommonResult handleException(BlockException exception)
{
return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
}
}
-
启动nacos,启动sentinel,去sentinel控制台添加
流控规则
配置说明:
表示1秒钟内查询次数大于1,就跑到我们自定义的处流,限流
1秒钟点击1下,没法发生降级。
超过上述,疯狂点击,返回了自己定义的限流处理信息,限流发生
2.按照Url地址限流以及后续处理
在刚刚新建的类
RateLimitController
添加以下方法
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl()
{
return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
}
Sentinel控制台配置,新增以下配置
访问
http://localhost:8401/rateLimit/byUrl
很慢的访问时
疯狂点击
http://localhost:8401/rateLimit/byUrl
成功的进行了限流
3.客户自定义限流处理逻辑
上面兜底代码存在的问题:
- 系统默认的,没有体现我们自己的业务要求。
- 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
- 每个业务方法都添加一个兜底的,那代码膨胀加剧。
- 全局统一的处理方法没有体现。
创建
CustomerBlockHandler
类用于自定义限流处理逻辑
public class CustomerBlockHandler
{
public static CommonResult handleException(BlockException exception){
return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler");
}
}
在
RateLimitController
添加新的方法
/**
* 自定义通用的限流处理逻辑
blockHandlerClass = CustomerBlockHandler.class
blockHandler = handleException
上述配置:找CustomerBlockHandler类里的handleException方法进行兜底处理
*/
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException")
public CommonResult customerBlockHandler()
{
return new CommonResult(200,"按客户自定义限流处理逻辑");
}
配置进一步说明
启动微服务后先访问一次
http://localhost:8401/rateLimit/customerBlockHandler
Sentinel控制台配置
启动微服务后先疯狂访问
http://localhost:8401/rateLimit/customerBlockHandler
更多注解属性说明
关于
@SentinelResource
注解最主要的两个用法:限流控制和熔断降级的具体使用案例介绍完了。另外,该注解还有一些其他更精细化的配置,比如忽略某些异常的配置、默认降级函数等等,具体可见如下说明:
-
value
:资源名称,必需项(不能为空) -
entryType
:entry 类型,可选项(默认为 EntryType.OUT) -
blockHandler / blockHandlerClass
:
blockHandle
r对应处理
BlockException
的函数名称,可选项。
blockHandler
函数访问范围需要是
public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为
BlockException
。
blockHandler
函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
blockHandlerClass
为对应的类的
Class
对象,注意对应的函数必需为
static
函数,否则无法解析。 -
fallback
:
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)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException
直接抛出
服务熔断功能
-
sentinel整合ribbon+openFeign+fallback
-
启动nacos和sentinel
创建服务提供者cloudalibaba-provider-payment9003/9004
-
新建cloudalibaba-provider-payment9003
-
修改pom文件,添加以下依赖
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.zhubayi.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
-
添加
application.yml
配置文件
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
-
主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003
{
public static void main(String[] args) {
SpringApplication.run(PaymentMain9003.class, args);
}
}
-
controller
package com.zhubayi.springcloud.alibaba.controller;
import com.zhubayi.springcloud.entities.CommonResult;
import com.zhubayi.springcloud.entities.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
@RestController
public class PaymentController
{
@Value("${server.port}")
private String serverPort;
public static HashMap<Long,Payment> hashMap = new HashMap<>();
static
{
hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
}
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
{
Payment payment = hashMap.get(id);
CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort: "+serverPort,payment);
return result;
}
}
-
9004模块也是,要记得改端口号和启动类
启动9003和9004以及nacos
测试
http://127.0.0.1:9003/paymentSQL/1
测试
http://127.0.0.1:9004/paymentSQL/1
新建
cloudalibaba-consumer-nacos-order84
服务消费者模块
cloudalibaba-consumer-nacos-order84
-
修改pom文件,添加以下的依赖
<dependencies>
<!--SpringCloud openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.zhubayi.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
-
新建
application.yml
配置文件
server:
port: 8400
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
-
启动类
OrderNacosMain84
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain84
{
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}
Ribbon系列
-
新建配置类
ApplicationContextConfig
,向容器注入Bean
@Configuration
public class ApplicationContextConfig
{
@Bean
@LoadBalanced
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
}
-
controller
package com.zhubayi.springcloud.alibaba.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zhubayi.springcloud.entities.CommonResult;
import com.zhubayi.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.sql.SQLException;
import java.util.concurrent.Executors;
@RestController
@Slf4j
public class CircleBreakerController
{
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback")
public CommonResult<Payment> fallback(@PathVariable Long id)
{
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
}
启动84端口,测试
http://192.168.0.159:8400/consumer/fallback/1
在刷新一下
没有任何配置
当访问
http://192.168.0.159:8400/consumer/fallback/4
给客户error页面,不友好
只配置fallback
修改controller,添加兜底方法
public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
完整
CircleBreakerController
类
@RestController
@Slf4j
public class CircleBreakerController
{
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback负责业务异常
public CommonResult<Payment> fallback(@PathVariable Long id)
{
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
}
测试
成功执行了兜底方法
只配置blockHandler
修改controller代码
@RestController
@Slf4j
public class CircleBreakerController
{
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常 handlerFallback兜底方法
@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler负责在sentinel里面配置的降级限流
public CommonResult<Payment> fallback(@PathVariable Long id)
{
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);
if (id == 4) {
throw new IllegalArgumentException ("非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"fallback,无此流水,exception "+e.getMessage(),payment);
}
public CommonResult blockHandler(@PathVariable Long id,BlockException blockException) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
添加流控规则
疯狂访问
http://192.168.0.159:8400/consumer/fallback/1
fallback和blockHandler都配置
-
修改
@SentinelResource
注解
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。fallback只负责业务异常 handlerFallback兜底方法,blockHandler负责在sentinel里面配置的降级限流
忽略属性
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
exceptionsToIgnore = {IllegalArgumentException.class}) //exceptionsToIgnore忽略这个异常,就是这个异常不处理
Feign系列
- pom文件记得添加openfeign的依赖
<!--SpringCloud openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 修改配置文件激活Sentinel对Feign的支持
# 激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
-
业务类
IPaymentService
/**
* 使用 fallback 方式是无法获取异常信息的,
* 如果想要获取异常信息,可以使用 fallbackFactory参数
*/
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)//调用中关闭9003服务提供者
public interface IPaymentService
{
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
PaymentFallbackService
@Component
public class PaymentFallbackService implements IPaymentService{
/**
* 远程调用
*
* @param id
* @return
*/
@Override
public CommonResult<Payment> paymentSQL(Long id) {
return new CommonResult<>(444,"服务降级返回",new Payment(id,"error"));
}
}
controller
//==================OpenFeign
//解决办法https://blog.csdn.net/kongliand/article/details/108058831
@Resource
private IPaymentService ipaymentService;
@GetMapping(value = "/consumer/openfeign/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
{
if(id == 4)
{
throw new RuntimeException("没有该id");
}
return ipaymentService.paymentSQL(id);
}
-
在主启动类上添加
EnableFeignClients
注解,开启远程调用
测试:把9003、9004都关了,然后访问
http://192.168.0.159:8400/consumer/openfeign/1
成功进行了降级。
规则持久化
-
一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化
-
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台
的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
-
修改
cloudalibaba-sentinel-service8401
-
添加依赖
<!--SpringCloud ailibaba sentinel-datasource-nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
-
修改配置文件
spring:
cloud:
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
完整配置文件
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
port: 8719
#sentinel持久化
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
feign:
sentinel:
enabled: true # 激活Sentinel对Feign的支持
-
添加Nacos业务规则配置
配置dataId、配置组和配置格式的由来
配置说明:
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。
启动8401后
刷新sentinel
发现业务规则有了
测试
快速访问测试接口
http://192.168.0.159:8401/rateLimit/byUrl
停止8401再看sentinel
发现流控规则没有了,重新启动8401再看sentinel
刚开始看还是没有,去访问
http://192.168.0.159:8401/rateLimit/byUrl
或者
http://127.0.0.1:8401/rateLimit/byUrl
,重新配置出现了,持久化验证通过