一、回顾
前面我们已经过了Sentinel的限流,和服务降级,主要涉及到SentinelDashboard的使用,和@SentinelResource注解的使用,主要用到的blockHandler,用来作为限流规则兜底方法;
这节学习Sentinel的服务熔断,将涉及到@SentinelResouce注解下的另一个属性值:fallback;
二、环境准备
1、快速创建服务提供者9003和9004,在使用Ribbon时候,可以使用负载均衡;
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.jiguiquan.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-provider-payment9003</artifactId>
<dependencies>
<!--springcloud alibaba nacos discovery 以后服务中将nacos和sentinel一起配-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--springcloud alibaba sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--springcloud alibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--公共服务模块依赖-->
<dependency>
<groupId>com.jiguiquan.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--springboot项目web和actuator最好一起走-->
<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>
<!--热部署devtools-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
2、配置文件application.yml:
server: port: 9003 spring: application: name: nacos-payment-provider cloud: nacos: discovery: server-addr: localhost:8848 management: endpoints: web: exposure: include: '*'
3、主启动类:略
4、业务类:PaymentController.java:
@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, "11111111111111111111111111111111"));
hashMap.put(2L, new Payment(2L, "22222222222222222222222222222222"));
hashMap.put(3L, new Payment(3L, "33333333333333333333333333333333"));
}
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;
}
}
5、创建一个服务消费者84:
application.yml:
server: port: 84 spring: application: name: nacos-order-consumer cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 port: 8719 service-url: nacos-user-service: http://nacos-payment-provider
6、Ribbon的负载均衡配置类:
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
7、业务类,CircleBreakerController.java
@RestController
public class CircleBreakerController {
private static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
public CommonResult<Payment> fallback(@PathVariable("id") Long id){
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+ id, CommonResult.class);
if (id == 4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常。。。。");
}else if (result.getData() == null){
throw new NullPointerException("NullPointerException, 该ID没有对应记录,空指针异常");
}
return result;
}
}
此时还没有配置任何的Sentinel服务熔断降级或限流规则;
8、正常使用,三种情况:



三、服务熔断fallback和限流blockHandler的使用
1、只配置fallback,fallback只负责业务异常
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback", fallback = "handlerFallback")
public CommonResult<Payment> fallback(@PathVariable("id") Long id){
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+ id, CommonResult.class);
if (id == 4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常。。。。");
}else if (result.getData() == null){
throw new NullPointerException("NullPointerException, 该ID没有对应记录,空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable("id") Long id, Throwable e){
Payment payment = new Payment(id, null);
return new CommonResult<>(444, "兜底异常handlerFallback,exception内容:"+ e.getMessage(), payment);
}
测试结果:


2、只配置blockHandler,blockhandler只负责sentinel控制台配置的违规行为(限流或降级)
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback", blockHandler = "blockHandler")
public CommonResult<Payment> fallback(@PathVariable("id") Long id){
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+ id, CommonResult.class);
if (id == 4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常。。。。");
}else if (result.getData() == null){
throw new NullPointerException("NullPointerException, 该ID没有对应记录,空指针异常");
}
return result;
}
public CommonResult blockHandler(@PathVariable("id") Long id, BlockException exception){
Payment payment = new Payment(id, null);
return new CommonResult<>(444, "blockHandler-sentinel限流,无此流水:"+ exception.getMessage(), payment);
}
blockHandler必须要配合Sentinel Dashboard使用,我们需要配置一个限流或者降级规则:

之后测试,刚开始两次的异常会直接打印出Error Page页面,但是2次之后,就会被降级规则所处理,进入blockHandler方法;

3、fallback和blockHandler都配置
直接写结论吧:当同时配置了fallback和blockHandler方法时,如果同时既满足了业务异常、又满足了sentinel服务降级,会进入blockHandler处理逻辑;
4、异常忽略exceptionsToIgnore
@SentinelResource(value = "fallback", fallback = "handlerFallback",
exceptionsToIgnore = {IllegalArgumentException.class, NullPointerException.class})
当向我上面这样配置的时候,则表明,配置的这两种异常会被直接忽略,即不会进入兜底方法,会直接显示ErrorPage;

四、Sentinel的规则持久化
1、前面我们使用Sentinel的时候,当我们在Sentinel控制台配置的各种规则后,如果服务重启,那么这些规则就消失了;
这样肯定是不行的,我们必须要对这些规则进行持久化;
2、Sentinel的规则持久化,需要依赖于Nacos——此时我们的Nacos数据已经持久化进mysql数据库;
目标:将限流规则持久化进Nacos,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel的流控规则持续有效;
3、操作步骤:
3.1、修改8401的pom文件,增加将sentinel配置持久化到nacos的依赖:
<!--springcloud alibaba sentinel-datasource-nacos 后续做持久化用到--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
3.2、修改application.yml配置文件:
server: port: 8401 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 #默认是8719端口,如果没占用,就会开始+1一直找,直到找到未被占用的端口 port: 8719 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的支持
3.3、到Nacos的控制台,添加业务规则配置,这里想想我们之前的Nacos作为配置中心的使用,一样:

但是这里的配置内容,需要解释一下: 跟我们在Sentinel控制台的配置是一一对应的;
[
{
"resource":"/rateLimit/byUrl", #资源名称
"limitApp":"default", #来源应用
"grade":1, #阈值类型:0:线程数、1:QPS
"count":1, #单机阈值
"strategy":0, #流控模式:1:直接、1:关联:2链路
"controlBehavior":0, #流控效果:0:快速失败,1:Warm Up,2:排队等待
"clusterMode":false #是否集群
}
]
3.4、此时,我们启动服务8401,然后访问对应的接口:
http://localhost:8401/rateLimit/byUrl
可以看到,我们之前在Nacos中配置的流控规则,就自动出现在了我们的Sentinel控制台中:

而且,当我们快速的访问上面的接口时,

说明我们通过Nacos持久化配置的Sentinel流控规则,也依然可以奏效,测试OK!
到这里,整个Sentinel的学习,终于结束了;
个人此项目代码地址(持续更新):



