目录
1.前言
前面一篇文章我们介绍了如何使用@SentinelResource
定义一个资源并且如何配置降级规则等,我们都知道,微服务之间的调用要么采用ribbon
借助RestTemplate
进行调用,要么是通过OpenFeign
声明式服务调用,涉及到跨服务调用,难免会出现网络故障,此时如果我们的接口报错,直接将报错信息展示到前端,客户看到很不友好。Sentinel提供了丰富的熔断之后的降级处理方法,本篇文章将Sentinel
与Ribbon
负载均衡、Sentinel
与OpenFeign
进行整合,分别介绍如何配置处理降级。
2.服务提供者搭建
新建一个子模块工程【springcloudalibaba-provider-payment9003
】和【springcloudalibaba-provider-payment9004
】作为我们的服务提供者,两个子模块代码除了端口不一致,其他一模一样,下面以【springcloudalibaba-provider-payment9003
】为例说明
【a】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>springcloud-alibaba-nacos</artifactId>
<groupId>com.bruce.springcloud-alibaba-nacos</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloudalibaba-provider-payment9003</artifactId>
<dependencies>
<!-- Alibaba-nacos服务发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<artifactId>nacos-client</artifactId>
<groupId>com.alibaba.nacos</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- nacos-client -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.2.0</version>
</dependency>
<!-- hystrix断路器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- openfeign客户端 ,默认集成并开启了ribbon负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--排除tomcat依赖 -->
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--undertow容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
【b】application.yml配置文件
server:
port: 9003
spring:
application:
name: springcloudalibaba-provider-payment
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*' #暴露端点配置
【c】主启动类
package com.bruce;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudAlibabaPaymentServiceApplication9003 {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaPaymentServiceApplication9003.class, args);
}
}
【d】controller业务层方法
package com.bruce.controller;
import com.bruce.pojo.JsonResult;
import com.bruce.pojo.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;
/**
* 模拟静态数据
*/
private static HashMap<Long, Payment> hashMap = new HashMap<Long, Payment>();
static {
hashMap.put(1L, new Payment(1L, "001"));
hashMap.put(2L, new Payment(2L, "002"));
hashMap.put(3L, new Payment(3L, "003"));
}
@GetMapping(value = "/payment/{id}")
public JsonResult<Payment> payment(@PathVariable("id") Long id) {
Payment payment = hashMap.get(id);
if (id.toString().equals("4")) {
System.out.println("输出异常");
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常......");
} else if (payment == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录......");
}
return new JsonResult(200, "serial: " + payment.getAccount() + ",serverPort: " + serverPort, payment);
}
}
【e】测试
启动9003
和9004
服务提供者,浏览器访问:http://localhost:9003/payment/1
http://localhost:9004/payment/3
可见,接口都成功返回数据,至此我们的服务提供方【9003和9004】也就搭建成功。
3.搭建服务消费者
新建子模块【springcloudalibaba-consumer-nacos-order84】,作为我们的服务消费者。
【a】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>springcloud-alibaba-nacos</artifactId>
<groupId>com.bruce.springcloud-alibaba-nacos</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloudalibaba-consumer-nacos-order84</artifactId>
<dependencies>
<!-- Alibaba-nacos服务发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<artifactId>nacos-client</artifactId>
<groupId>com.alibaba.nacos</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- nacos-client -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.2.0</version>
</dependency>
<!-- hystrix断路器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- openfeign客户端 ,默认集成并开启了ribbon负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--排除tomcat依赖 -->
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--undertow容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
【b】application.yml配置文件
server:
port: 84
spring:
application:
name: springcloudalibaba-consumer-nacos-order
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置nacos服务器地址
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
【c】主启动类
package com.bruce;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudAlibabaConsumerOrderServiceApplicaiton84 {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaConsumerOrderServiceApplicaiton84.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
【d】业务controller方法
import javax.annotation.Resource;
@RestController
public class CircleBreakerController {
/**
* 指定服务提供者的微服务名称
*/
private static final String SERVICE_URL = "http://springcloudalibaba-provider-payment";
/**
* 负载均衡调用类
*/
@Resource
private RestTemplate restTemplate;
/**
* 没有配置fallback和blockHandler
*/
@RequestMapping("/consumer/noFallback/{id}")
@SentinelResource(value = "noFallback") //没有配置
public JsonResult<Payment> noFallback(@PathVariable Long id) {
JsonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/payment/" + id, JsonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常......");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,暂未找到对应记录......");
}
return result;
}
}
【e】测试
启动服务提供者9003
和9004
项目以及服务消费者84服务,当nacos服务注册列表看到三个服务都成功注册之后
浏览器访问:http://localhost:84/consumer/noFallback/1,
根据运行结果,我们发现,服务消费者84已经成功实现了对服务提供者8003和8004的负载均衡调用,默认采用轮训的方式进行调用,至此我们的服务消费方也算搭建成功。
下面我们详细介绍五种不同配置下的服务熔断处理方法。
4.Sentinel服务熔断 - 没有配置fallback和blockHandler
【a】控制层增加如下代码
/**
* 没有配置fallback和blockHandler
*/
@RequestMapping("/consumer/noFallback/{id}")
@SentinelResource(value = "noFallback") //没有配置
public JsonResult<Payment> noFallback(@PathVariable Long id) {
JsonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/payment/" + id, JsonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常......");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,暂未找到对应记录......");
}
return result;
}
【b】测试
浏览器访问:http://localhost:84/consumer/noFallback/2
当我们id = 4时,根据上面的程序判断将会抛出非法参数异常,浏览器访问:http://localhost:84/consumer/noFallback/4
当我们id = 5时,根据上面的程序判断将会抛出空指针异常,浏览器访问:http://localhost:84/consumer/noFallback/5
【c】小总结
由上面的测试结果可以看到,当我们什么都没有配置的时候,如果发生异常,那么异常信息将直接展示到前台给客户,很不友好。
5.Sentinel服务熔断 - 只配置fallback
【a】业务层增加如下方法
/**
* fallback只负责业务异常
*/
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback", fallback = "handlerFallback")
public JsonResult<Payment> fallback(@PathVariable Long id) {
JsonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/payment/" + id, JsonResult.class, id);
return result;
}
/**
* 业务异常兜底降级方法
*/
public JsonResult handlerFallback(@PathVariable Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new JsonResult<>(444, "[业务异常兜底降级方法],exception内容: " + e.getMessage(), payment);
}
【b】测试
浏览器访问:http://localhost:84/consumer/fallback/1
当我们id = 4时,根据上面的程序判断将会抛出非法参数异常,浏览器访问:http://localhost:84/consumer/fallback/4
当我们id = 5时,根据上面的程序判断将会抛出空指针异常,浏览器访问:http://localhost:84/consumer/fallback/5
【c】小总结
当我们配置了fallback属性后,如果程序发生Java异常,那么错误信息将不会展示到前台展示,它将执行我们指定的业务异常兜底降级方法,错误信息可以自定义,比如提示 “服务器异常,请稍后重试” 等等,比起前面一种方式显然友好很多。
6.Sentinel服务熔断 - 只配置blockHandler
【a】业务层增加如下方法
/**
* blockHandler只负责sentinel控制台配置的违规情况,业务运行时异常sentinel不管,该抛出还是抛出
*/
@RequestMapping("/consumer/withBlockHandler/{id}")
@SentinelResource(value = "withBlockHandler", blockHandler = "blockHandler")
public JsonResult<Payment> withBlockHandler(@PathVariable Long id) {
JsonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/payment/" + id, JsonResult.class, id);
return result;
}
/**
* sentinel控制台配置违反兜底降级方法
*/
public JsonResult blockHandler(@PathVariable Long id, BlockException blockException) {
return new JsonResult<>(445, "[sentinel控制台配置违反兜底降级方法],无此流水: blockException " + blockException.getMessage(), new Payment(id, "null"));
}
【b】Sentinel配置
【c】测试
浏览器访问:http://localhost:84/consumer/withBlockHandler/1
可以看到,如果一秒钟访问一次,接口是没有问题的。但是如果我们1秒内访问多次该接口:http://localhost:84/consumer/withBlockHandler/1:
可以看到,当违反了sentinel控制台配置的降级规则时,将会触发blockHandler方法指定的降级处理逻辑。
下面我们看看blockHandler是否会管业务运行时异常?
当我们id = 4时,根据上面的程序判断将会抛出非法参数异常,浏览器访问:http://localhost:84/consumer/withBlockHandler/4
当我们id = 5时,根据上面的程序判断将会抛出空指针异常,浏览器访问:http://localhost:84/consumer/withBlockHandler/5
我们发现,当配置了blockHandler的时候,它只会针对违反sentinel控制台的降级规则生效,对于业务运行时抛出的异常,blockHandler是不会进行降级处理的。
【d】小总结
当配置了blockHandler的时候,它只会针对违反sentinel
控制台降级规则的情况下生效,对于业务运行时抛出的异常,blockHandler
是不会进行降级处理的,依旧会把运行时异常直接展示给客户页面,不太友好。
7.Sentinel服务熔断 - 配置fallback和blockHandler
【a】业务层增加如下方法
/**
* blockHandler只负责sentinel控制台配置的违规情况,业务运行时异常sentinel不管,该抛出还是抛出
* fallback只负责业务异常
*/
@RequestMapping("/consumer/withBlockHandlerAndFallback/{id}")
@SentinelResource(value = "withBlockHandlerAndFallback", fallback = "handlerFallback", blockHandler = "blockHandler",)
public JsonResult<Payment> withBlockHandlerAndFallback(@PathVariable Long id) {
JsonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/payment/" + id, JsonResult.class, id);
return result;
}
/**
* 业务异常兜底降级方法
*/
public JsonResult handlerFallback(@PathVariable Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new JsonResult<>(444, "[业务异常兜底降级方法],exception内容: " + e.getMessage(), payment);
}
/**
* sentinel控制台配置违反兜底降级方法
*/
public JsonResult blockHandler(@PathVariable Long id, BlockException blockException) {
return new JsonResult<>(445, "[sentinel控制台配置违反兜底降级方法],无此流水: blockException " + blockException.getMessage(), new Payment(id, "null"));
}
【b】sentinel配置
【c】测试
浏览器访问:http://localhost:84/consumer/withBlockHandlerAndFallback/2
可以看到,如果一秒钟访问一次,接口是没有问题的。但是如果我们1秒内访问多次该接口:
可以看到,当违反了sentinel控制台配置的降级规则时,将会触发blockHandler方法指定的降级处理逻辑。 下面我们来看看业务异常怎么处理?同上,
当我们id = 4时,根据上面的程序判断将会抛出非法参数异常,浏览器访问:http://localhost:84/consumer/withBlockHandlerAndFallback/4
当我们id = 5时,根据上面的程序判断将会抛出空指针异常,浏览器访问:http://localhost:84/consumer/withBlockHandlerAndFallback/5
【c】小总结
由前面的测试结果可以看到,当同时配置了fallback和blockHandler的时候,两者并不冲突,各处理各的。blockHandler处理sentinel控制台违反规则的降级处理;fallback处理业务运行时异常的降级处理。
注意:如果是疯狂访问:http://localhost:84/consumer/withBlockHandlerAndFallback/5,很明显,程序会抛出异常,并且也违反了sentinel控制台降级规则,小伙伴们注意,这时候,是以blockHandler降级处理为准的。
8.Sentinel服务熔断 - 配置exceptionsToIgnore
【a】业务层方法增加如下配置:使用exceptionsToIgnore属性来排除某些异常,意思就是假如程序抛出该异常时,不再进入业务异常降级处理方法,没有降级效果。
/**
* blockHandler只负责sentinel控制台配置的违规情况,业务运行时异常sentinel不管,该抛出还是抛出
* fallback只负责业务异常
*/
@RequestMapping("/consumer/withBlockHandlerAndFallback/{id}")
@SentinelResource(value = "withBlockHandlerAndFallback", fallback = "handlerFallback", blockHandler = "blockHandler",
exceptionsToIgnore = {
IllegalArgumentException.class})
public JsonResult<Payment> withBlockHandlerAndFallback(@PathVariable Long id) {
JsonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/payment/" + id, JsonResult.class, id);
return result;
}
【b】测试
当我们id = 4时,根据上面的程序判断将会抛出非法参数异常,也即是我们上面忽略的这个异常,浏览器访问:http://localhost:84/consumer/withBlockHandlerAndFallback/4添加链接描述
可以看到,当程序抛出IllegalArgumentException异常时,将不再走fallback降级处理方法。
浏览器访问:http://localhost:84/consumer/withBlockHandlerAndFallback/5
可以看到,处理上面排除的异常,其他异常还是继续会走降级处理。
【c】小总结
exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。使用exceptionsToIgnore属性来排除某些异常,意思就是假如程序抛出该异常时,不再进入业务异常降级处理方法,没有降级效果。
9.Sentinel服务熔断OpenFeign
前面我们介绍了Sentinel和Ribbon负载均衡结合起来,并通过示例详细介绍了五种服务降级的配置以及各自产生的效果。接下来我们介绍一些Sentinel整合OpenFeign远程服务调用实现服熔断降级。所以需要对【springcloudalibaba-consumer-nacos-order84】进行改造:
【a】pom.xml依赖:加入open-feign依赖
<!--SpringCloud openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
【b】application.yml配置文件:加入feign对sentinel的支持
feign:
sentinel:
enabled: true # 激活Sentinel对Feign的支持
【c】主启动类加上注解
@EnableDiscoveryClient
@SpringBootApplication
//开启远程调用
@EnableFeignClients
public class SpringCloudAlibabaConsumerOrderServiceApplicaiton84 {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaConsumerOrderServiceApplicaiton84.class, args);
}
}
【d】编写FeignClient接口
import com.wsh.springcloud.alibaba.feign.fallback.PaymentFeignClientFallback;
import com.wsh.springcloud.common.JsonResult;
import com.wsh.springcloud.entity.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "springcloudalibaba-provider-payment", fallback = PaymentFeignClientFallback.class)
public interface PaymentFeignClient {
@GetMapping(value = "/payment/{id}")
Object payment(@PathVariable("id") Long id);
}
【e】编写FeignClient接口失败回调类
import com.wsh.springcloud.alibaba.feign.PaymentFeignClient;
import com.wsh.springcloud.common.JsonResult;
import com.wsh.springcloud.entity.Payment;
import org.springframework.stereotype.Component;
@Component
public class PaymentFeignClientFallback implements PaymentFeignClient {
@Override
public Object payment(Long id) {
return new JsonResult<>(500, "服务降级返回,---PaymentFallbackService", new Payment(id, "errorSerial"));
}
}
【f】业务controller新增如下方法
@Resource
private PaymentFeignClient paymentFeignClient;
@GetMapping(value = "/consumer/openfeign/payment/{id}")
public Object payment(@PathVariable("id") Long id) {
Object payment = paymentFeignClient.payment(id);
return payment;
}
【g】测试
重启服务消费者84服务,浏览器访问:http://localhost:84/consumer/feignFallback/1
可见,接口成功返回数据,证明我们远程调用成功。
下面我们关闭两个服务提供者9003和9004,再次访问:http://localhost:84/consumer/feignFallback/1
可以看到,此时调用了服务降级,说明OpenFeign的fallback处理逻辑生效了,这就实现了sentinel与openfeign的服务降级处理。
10.总结
本篇文章通过将Sentinel
与ribbon
和openfeign
进行整合,实现了跨服务调用之间的接口的熔断降级处理,并分析了几种不同的配置情况下,Sentinel
是如何处理降级的,在实际项目中,推荐fallback
和blockHandler都进行配置,这样就能兼容业务异常和sentinel流控规则违反情况的降级处理。