Cloud Alibaba Sentinel——服务熔断

一、回顾

前面我们已经过了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、正常使用,三种情况:

80.jpg

81.jpg

82.jpg


三、服务熔断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);
}

测试结果:

83.jpg

84.jpg

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使用,我们需要配置一个限流或者降级规则:

85.jpg

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

86.jpg

3、fallback和blockHandler都配置

直接写结论吧:当同时配置了fallback和blockHandler方法时如果同时既满足了业务异常、又满足了sentinel服务降级,会进入blockHandler处理逻辑

4、异常忽略exceptionsToIgnore

@SentinelResource(value = "fallback", fallback = "handlerFallback", 
        exceptionsToIgnore = {IllegalArgumentException.class, NullPointerException.class})

当向我上面这样配置的时候,则表明,配置的这两种异常会被直接忽略,即不会进入兜底方法,会直接显示ErrorPage;

87.jpg


四、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作为配置中心的使用,一样:

88.jpg

但是这里的配置内容,需要解释一下: 跟我们在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控制台中:

89.jpg

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

90.jpg

说明我们通过Nacos持久化配置的Sentinel流控规则,也依然可以奏效,测试OK!

到这里,整个Sentinel的学习,终于结束了;

个人此项目代码地址(持续更新):

https://github.com/jiguiquan/cloud2020

jiguiquan@163.com

文章作者信息...

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐