一、回顾
前面我们已经过了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的学习,终于结束了;
个人此项目代码地址(持续更新):