1.官网

英文:https://github.com/alibaba/Sentinel

中文:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

文档:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

 

2.简要说明

  Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。2012年,Sentinel诞生于阿里巴巴,其主要目标是流量控制。2013-2017年,Sentinel迅速发展,并成为阿里巴巴所有微服务的基本组成部分。 它已在6000多个应用程序中使用,涵盖了几乎所有核心电子商务场景。2018年,Sentinel演变为一个开源项目。2020年,Sentinel Golang发布。更详细的还是看官网最好。

  

3.sentinel的组成

  Sentinel 的使用可以分为两个部分:

  • 控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。

  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持

4.sentinel的下载

  https://github.com/alibaba/Sentinel/releases

  这里选择的1.7.0版本

  

 

 

 4.运行

  下载下来是一个jar包,需要JDK1.8环境

   执行命令 java -jar sentinel-dashboard-1.7.0.jar

 

   默认端口是8080,注意端口冲突问题

5.访问sentinel控制台

  http://localhost:8080/#/login

  

   默认账号密码都是sentinel

  出现此界面,表示sentinel下载运行成功

6.使用sentinel来保护项目

  上面,我们运行sentinel成功,下面,我们在项目中做一个最简单的使用。注册中心使用的nacos。

6.1创建一个模块

  

 6.2依赖

  nacos依赖和sentinel依赖

  <!--服务注册-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--sentinel持久化-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

        <!--sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

完整

    <dependencies>

        <!--服务注册-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--sentinel持久化-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

        <!--sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <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>

        <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>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

6.3配置文件

 

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service  #将会作为注册到注册中心的服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos注册中心的地址
    sentinel:
      transport:
        dashboard: localhost:8080     #sentinel控制台地址
        port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
management:
  endpoints:
    web:
      exposure:
        include: '*'

这里,配置了nacos注册中心地址,配置了sentinel的相关配置

6.4主启动类

 

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @Classname PaymentMain9001
 * @Description TODO
 * @Date 2021/6/18 0018 下午 2:20
 * @Created by jcc
 */
@SpringBootApplication
@EnableDiscoveryClient
public class MainApp8401 {
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class,args);
    }
}

 

6.5业务类

   业务类提供两个简单的接口

package com.atguigu.springcloud.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Classname FlowLimitController
 * @Description TODO
 * @Date 2021/6/24 0024 下午 4:11
 * @Created by jcc
 */

@RestController
public class FlowLimitController
 {
    @GetMapping("/testA")
    public String testA() {
        return "------testA";
     }
     
     @GetMapping("/testB")
    public String testB() {
       return "------testB";
    }
 }

 

6.6启动

 启动nacos,启动sentinel,启动该模块,登录sentinel控制台

  访问testA和testB接口

 刷新 sentinel控制台

  

   此时,我们可以看到sentinel控制台出现了testA和testB。

  左侧菜单提供了很多的功能,这个后面再一一讲解

 7.流量控制

  https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

  流量控制可以对特定的请求资源进行流量控制,设置相应的规则,当访问量超出规则时,会对这个请求进行流量控制,直接返回。

  下图是sentinel控制台-流量控制页面

 

 选择流控规则,点击新增流控规则,点击高级选项

 

 7.1流控规则内容说明

   1)资源名

      唯一名称,默认请求路径。也可以通过注解配置

    2)针对来源

      针对哪个调用者进流量控制,填写服务名称即可,默认default-不区分来源,针对所有调用者

    3)阀值类型

      QPS:每秒请求数,但调用量大于这个值的时候,进行限流

      线程数:处理该请求的线程数,处理该API的线程数大于这个值的时候,进行限流

    4)是否集群

    5)流控模式

      直接:达到阀值时,直接限流

      关联:本请求达到阀值,对和他相关的请求进行限流

      链路:只记录指定链路上的流量,如果达到阀值,则限流。指定链路,比如/testA有很多地方调用了,我只限制testB对他的调用为1秒最多调用1次,其它的队testA的调用则不管

    6)流控效果-阀值类型QPS才有这个选项

      快速失败:达到阀值,直接失败

      Warm Up:预热。

      排队等待:允许排队,让请求匀速通过

 

下面来一一测试不同的配置的效果

 

7.1.1流量控制-测试1

  对/testA进行流量控制

  阀值类型为QPS

  每秒请求书限制为1个

  流控模式为直接

  流控效果为快速失败

  也就是设置testA1秒内访问量限制为1次,超过直接返回

 

   添加成功后,访问testA这个请求

   1)访问方式:浏览器直接访问一次

    成功

    

 

  2)访问方式:一秒内快速访问两次

  第一次请求成功,第二次请求失败,sentinel返回错误信息,这个错误信息是sentinel自带的,如何自定义返回信息,后面再说

  

  因为我们设置了1秒内访问一次的阀值,这里超过了阀值,所以被限流了

7.1.2流量控制-测试

  对/testA进行流量控制

  阀值类型为线程数

  线程数限制为1

  流控模式为直接

  流控效果无法选择

  也就是设置testA访问线程数为1,超出直接返回

   

 

   直接对上面的规则编辑就可以,编辑完成后,立即生效

  

  访问testA这个请求

   1)访问方式:浏览器1秒内直接访问一次或者多次

    成功

    这就是线程数和QPS的区别,QPS是对请求数进行限制,线程数是对线程进行限制,只提供1个线程来处理这个请求,不管请求数多少,只要这个线程处理的过来,就会处理

    

  2)访问方式:在浏览器1秒内快速访问这个请求一次

    对接口testA做一点点修改,让他等待2s  


@GetMapping("/testA")
public String testA() throws InterruptedException {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName());
return "------testA";
}
 

    

   3)访问方式:在浏览器1秒内快速访问这个请求多次

   

   第二次立马返回失败信息,因为我们设置了1个线程的阀值,超过这个阀值,就被限流了

 

7.1.3流量控制-测试

  对/testA进行流量控制

  阀值类型为QPS

  单机阀值限制为1

  流控模式为关联-关联对象为/testB

  流控效果快速失败

  当testB超出阀值时,testA将被限制

  设置testB访问1秒限制1次,超过的话限制testA,直接返回

  

 

   1)浏览器分别访问testA 和testB一次

     把testA睡眠2秒的代码去掉

  

    @GetMapping("/testA")
    public String testA() throws InterruptedException {
        System.out.println(Thread.currentThread().getName());
        return "------testA";
     }


     @GetMapping("/testB")
    public String testB() {
       return "------testB";
    }

 

    成功

    

 

 

 

    2)浏览器1秒内访问testB多次,再访问testA

    

    

  当testB的访问超过设定的阀值1秒1次的访问量时,testA被限流

 

 7.1.4流量控制-测试 

  对/testA进行流量控制

  阀值类型为QPS

  单机阀值限制为1

  流控模式为链路

  流控效果快速失败

  对testB调用testA进行限制,1秒内testB最多允许调用testA1次,超多则限流,直接返回。其它的对testA的调用不做限制

  

 

 7.1.5流量控制-测试 

  对/testA进行流量控制

  阀值类型为QPS

  单机阀值限制为6

  流控模式为直接

  流控效果快速失败

   预热时长为5秒

  这里对testA进行限流,阀值为每秒6次,流控效果为warm up,时长为5秒。这里就有一个过程,最开始的阀值不是6,而是阀值6除以冷加载因子(默认为3),也就是说刚开始阀值是2,当请求量进来超过2时,超过的请求被限制直接返回。经过5秒后,阀值变为6。这种模式适合秒杀这种,访问量平常很低或者没有,某一刻突然大量的访问。这样保护服务,不让它被突然的大量访问压死。

  

 

   1)浏览器访问testA一次

  成功

  

   2)快速访问testA,每秒大于2次,连续访问8秒

    在前5秒的访问中,每秒最多只能访问两次,超过的被限制直接返回,过了5秒后,每秒访问量不超过6次,均可以返回。说明前5秒阀值是2,超过5秒后,阀值变为6了

 

 7.1.6流量控制-测试 

  对/testA进行流量控制

  阀值类型为QPS

  单机阀值限制为2

  流控模式为直接

  流控效果排队等待

  超时时间为1秒

  这里限制testA每秒2次的访问量,超过的,排队等待,等待超时时间为1秒

  

 

  1)测试,使用测试软件发送请求,发送10次请求,间隔时间为0.1秒

 

@GetMapping("/testA")
    public String testA() throws InterruptedException {
        System.out.println(System.currentTimeMillis() + "---" + Thread.currentThread().getName());
        return "------testA";
     }

控制台打印结果,每次打印时间间隔大致为0.5秒,说明请求以0.5秒一次的速度通过

1624867018539---http-nio-8401-exec-2
1624867019039---http-nio-8401-exec-1
1624867019537---http-nio-8401-exec-4
1624867020037---http-nio-8401-exec-5
1624867020539---http-nio-8401-exec-6
1624867021037---http-nio-8401-exec-7
1624867021538---http-nio-8401-exec-8
1624867022037---http-nio-8401-exec-9
1624867022538---http-nio-8401-exec-10
1624867023039---http-nio-8401-exec-3

 

 

 

 8.熔断降级

   https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

   除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用

      现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

 

 

 

8.1策略

  

 

   

 

     1)RT:1秒内,请求数目大于最小请求数目(默认为5),且平均的响应时长大于设置的时长,服务熔断降级,时间窗口指的是熔断的时间

      

    2)异常比例:1秒内,请求数目大于最小请求数目(默认为5),异常比例大于设置比例,服务熔断降级,时间窗口指的是熔断的时间

      

    3)异常数:1分钟内,请求数大于最小请求数目(默认5),且请求异常数大于设置的异常数,服务熔断降级,时间窗口指的是熔断的时间

 

     

 

  8.热点参数限流

  https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81

    前面流量控制是针对某个请求。这里可以进行进一步的细化。设置当某个请求附带哪个参数的时候,才进行限流。

   

 

 

 

  8.1写一个带参数接口

  接口定义了两个参数,id和name

  

     @GetMapping("/testD")
     @SentinelResource(value = "testD")
     public String testD(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "------testD-----id=" + id + "----name=" + name ;
     }

 

  注意,这里使用到了@SentinelResource注解,这个注解这里value属性是定义资源名字

 

  8.2配置规则

  配置对testD进行限流,只有当他带有第一个参数id时,才进行限流,每秒限流1次访问。注意,这里使用的资源名是testD(@SentinelResource注解配置的资源名),而不是/testD(资源路径)。因为我发现不使用@SentinelResource注解定义资源名字,直接使用路径配置,不生效。

 

 

1)测试1

    浏览器1秒内访问testD接口多次,不带参数,每次都能成功返回

 2)测试2

         浏览器1秒内访问testD接口多次,携带参数name,每次都能成功返回

 3)测试3

         浏览器1秒内访问testD接口多次,携带参数id,限流

 

8.3自定义限流时返回信息

    上面限流直接返回的是错误页面,不友好,可以使用@SentinelResource设置返回信息

    在@SentinelResource注解添加了属性blockHandler,这个属性就是指当限流时,调用哪个方法去处理

     @GetMapping("/testD")
     @SentinelResource(value = "testD",blockHandler = "myResult")
     public String testD(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "aaaa------testD-----id=" + id + "----name=" + name ;

     }

  限流时处理的方法,方法名是blockHandler属性,该方法和上面testD的返回值类型需要一样,参数列表需要和testD一样,且需要多一个参数BlockException

public String myResult(String id, String name, BlockException exception){
        return "限流了a";

     }

    此时,在1秒内返回多次,页面返回的就是处理方法的返回值

    

   8.4高级设置-设置参数值

  

           

 

         现在的规则是,请求testD带有参数id且id不等于1的时候,1秒限制1次访问,当id为1的时候,限制1秒访问5次

      1)测试

        1秒内访问testD两次,id不等于1,第二次就被限流了

      

    2)测试

      1秒内访问testD两次,id=1,成功

    

     3)测试

    1秒内访问testD多次(大于5),id=1,超过5次时,限流

 

  9.系统自适应限流

    前面讲的限流都是针对某一个资源的,如果要针对整个系统,可以配置系统规则。

    

   

 

     

     这里具体的配置例子就不做了。

10.注解@SentinelResource

     上面用到了它的两个属性

    value:资源名

    blockHandler:处理方法

    下面会用到它的CustomerBlockHandler属性

    下面有两个接口testC和testD,他们的处理方法分别为myResult1myResult2,处理方法和业务代码在一起

@GetMapping("/testC")
     @SentinelResource(value = "testC",blockHandler = "myResult1")
     public String testC(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "------testC-----id=" + id + "----name=" + name ;
     }

     @GetMapping("/testD")
     @SentinelResource(value = "testD",blockHandler = "myResult2")
     public String testD(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "aaaa------testD-----id=" + id + "----name=" + name ;

     }

     public String myResult1(String id, String name, BlockException exception){
         return "testC限流了";

     }

     public String myResult2(String id, String name, BlockException exception){
         return "testD限流了";

     }

   把处理方法分离出去

1)创建一个类,名字随便取,把两个处理方法移到里面去 

 方法必须是静态

package com.atguigu.springcloud.controller;

import com.alibaba.csp.sentinel.slots.block.BlockException;

/**
 * @Classname MyHandler
 * @Description TODO
 * @Date 2021/6/29 0029 下午 4:38
 * @Created by jcc
 */
public class CustomerBlockHandler {
    public static String myResult1(String id, String name, BlockException exception){
        return "myhandler-testC限流了";
    }

    public static String myResult2(String id, String name, BlockException exception){
        return "myhandler-testD限流了";

    }
}

 

2)@SentinelResource添加属性blockHandlerClass

  这样子处理方法就可以先找到刚才创建的类CustomerBlockHandler .class,再找到里面的具体的处理方法。这样子,处理方法和业务就分开了。

     @GetMapping("/testC")
     @SentinelResource(value = "testC",blockHandlerClass = CustomerBlockHandler.class ,blockHandler = "myResult1" )
     public String testC(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
        return "------testC-----id=" + id + "----name=" + name ;
    }


     @GetMapping("/testD")
     @SentinelResource(value = "testD",blockHandlerClass = CustomerBlockHandler.class ,blockHandler = "myResult2")
     public String testD(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "aaaa------testD-----id=" + id + "----name=" + name ;

     }

 

下面我们介绍下fallback属性和blockHandler属性的区别

fallback是对系统runtimeexception作出处理,blockHandler是对违反sentinel配置的一系列规则作出处理

我创建两个服务提供者9003和9004,一个服务调用者84

10.1创建服务提供者9003

10.1.1创建模块

  

10.1.2依赖

 <dependencies>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <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>

        <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>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

10.1.4配置文件

 

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider  #将会作为注册到注册中心的服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

 

10.1.5启动类

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9003.class,args);
    }
}

 

10.1.6业务类

 

@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;
            }



        }

 

10.2 服务提供者9004的创建和9003除了端口都一样

10.3服务消费者84的创建  

 10.3.1创建模块

 

 10.3.2 依赖

    <dependencies>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <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>

        <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>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

10.3.3配置文件

 

server:
  port: 84

spring:
  application:
    name: nacos-order-consumer  #将会作为注册到注册中心的服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址
    sentinel:
      transport:
        dashboard: localhost:8080     #sentinel控制台地址
        port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
service-url:
  nacos-user-service: http://nacos-payment-provider

10.3.4主启动类

 

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain84 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain84.class,args);
    }
}

 

10.3.5业务类

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

 

package com.atguigu.springcloud.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;

import lombok.extern.slf4j.Slf4j;
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;


@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") //没有配置
            //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
             @SentinelResource(value = "fallback",blockHandler = "blockHandler",fallback = "handlerFallback") //blockHandler只负责sentinel控制台配置违规
//            @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
//                    exceptionsToIgnore = {IllegalArgumentException.class})
            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;
            }
          
            //fallback
            public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
                Payment payment = new Payment(id,"null");
                return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
            }
          
            //blockHandler
            public CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {
                Payment payment = new Payment(id,"null");
                return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
            }


        }
         
         

 10.4测试

  启动9003和9004

      在sentinel配置规则1秒内只能访问1次

  

 

  1)84的业务类里面的注解使用@SentinelResource(value = "fallback") //这里没有配置fallback和blockHandler属性

    #访问http://localhost:84/consumer/fallback/1,正常

    

 

    #访问http://localhost:84/consumer/fallback/4,返回非法参数异常报错页面

    

 

    #访问http://localhost:84/consumer/fallback/5,返回空指针异常报错页面

    

 

     #1秒内访问http://localhost:84/consumer/fallback/1多次,返回错误页面

 

    

 

 2)84的业务类里面的注解使用@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback负责业务异常      注意,修改这个配置,如果重启了84模块,需要重新在sentinel控制台添加配置1秒最多访问一次

 

   #访问http://localhost:84/consumer/fallback/1,正常

   #访问http://localhost:84/consumer/fallback/4,返回fallback处理方法信息

    

       #访问http://localhost:84/consumer/fallback/5,返回fallback处理方法信息

  

      #1秒内访问http://localhost:84/consumer/fallback/1多次,返回错误页面

 

  以上测试说明,fallback属性对页面异常进行了处理,对于超出sentinel规则没有做出处理

 3)84的业务类里面的注解使用@SentinelResource(value = "fallback",blockHandler = "blockHandler")//blockHandler 负责超出sentinel规则的处理    

  #访问http://localhost:84/consumer/fallback/1,正常

   #访问http://localhost:84/consumer/fallback/4,返回错误页面

  #访问http://localhost:84/consumer/fallback/5,返回错误页面

  #1秒内访问http://localhost:84/consumer/fallback/1多次,返回blockHandler处理信息

  

 4)@SentinelResource(value = "fallback",blockHandler = "blockHandler",fallback = "handlerFallback")

  采用这样的配置,业务异常和超出sentinel规则都会被处理

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

  在4的基础上加了 exceptionsToIgnore属性,就是说fallback对业务异常的处理,忽略IllegalArgumentException这个异常,也就是发生IllegalArgumentException异常时,fallback不做处理

   #访问http://localhost:84/consumer/fallback/4,返回非法参数异常报错页面

    

          #访问http://localhost:84/consumer/fallback/5,返回fallback处理方法信息  

10.5fallback处理方法和业务逻辑解耦

   blockHandler属性使用blockHandlerClass属性把处理方法放到一个专门的类里面,fallback属性的处理的方法同样可以通过fallbackClass属性放到一个单独的类里面,具体参考前面的blockHandler的处理。

 

11.sentinel配置的持久化

11.1简要的持久化

  这种持久化,是吧sentinel的配置放到nacos去配置,而不是在sentinel控制台配置好,自动持久化到nacos,配置起来比较麻烦

  https://blog.csdn.net/wangjinb/article/details/107559200

11.2修改sentinel来实现持久化

  https://blog.csdn.net/weixin_42211601/article/details/113062732?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-1&spm=1001.2101.3001.4242

  膜拜大神操作-试着弄了一下,好用