Sentinel 入门学习记录

1.Sentinel是什么?

随着分布式系统变得越来越流行,服务之间的可靠性变得比以往任何时候都更加重要。Sentinel是强大的流控制组件,以“流”为切入点,涵盖多个领域,包括流控制,并发限制,电路中断和自适应系统保护,以确保微服务的可靠性。
一句话讲就是Spring Cloud Alibaba用来替换之前的Hystrix的技术。

2.有什么用?

用来做系统流量控制、熔断降级、系统的负载保护等。
在这里插入图片描述

3.下载并启动Sentinel

1.下载地址
2.启动

java -jar sentinel-dashboard-1.7.2.jar

3.访问Dashboard,用户名密码都是sentinel
在这里插入图片描述

4.测试准备

1.新建一个springboot项目
2.引入依赖

<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--     sentinel-datasource-nacos 后续持久化用   -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <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>

3.启动类添加注解@EnableDiscoveryClient

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

4.application.properties配置

server.port=8401

spring.application.name=cloudalibaba-sentinel-service
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.transport.port=8719
spring.cloud.sentinel.datasource.dsl.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.dsl.nacos.data-id=${spring.application.name}
spring.cloud.sentinel.datasource.dsl.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.dsl.nacos.data-type=json
spring.cloud.sentinel.datasource.dsl.nacos.rule-type.=flow

management.endpoints.web.exposure.include=*

5.提供测试接口

@RestController
public class FlowLimitController {

    private static Logger log = LoggerFactory.getLogger(FlowLimitController.class);

    @GetMapping("/testA")
    public String testA(){
//        try {
//            TimeUnit.MILLISECONDS.sleep(800);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        return "testA-----";
    }

    @GetMapping("/testB")
    public String testB(){
        log.info(Thread.currentThread().getName() + "...testB ");
        return "testB   -----";
    }
    }

6.启动sentinelnacos
不懂nacos可以参考

5.测试流控规则

在这里插入图片描述

  • 资源名:唯一名称,默认请求路径
  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
  • 阈值类型/单机阈值
    • QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流
    • 线程数:当调用该api的线程数达到阈值的时候,进行限流
  • 流控模式
    • 直接:api达到限流条件时,直接限流
    • 关联:当关联的资源达到阈值时就限流自己。
      A接口与B接口关联,当B接口到达阈值,让A接口限流起到保护B接口的作用。 例如支付接口与下单接口,当支付接口到达阈值,让下单接口限流,起到保护支付接口的作用。
    • 链路:只记录指定链路上的流量(指定资源入口资源进来的流量,如果达到阈值,就进行限流)
  • 流控效果
    • 快速失败:直接失败抛异常
    • Warm up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
      一般用于类似秒杀的功能。
    • 排队等候:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效。

5.1测试QPS到达阈值

配置如下:
在这里插入图片描述
当你连续点击看到如下限流提示(1s请求数量超过配置阈值)
在这里插入图片描述

5.2测试线程数到达阈值

配置如下:
在这里插入图片描述
为了方便测试让每个被调用的方法睡眠一会

@GetMapping("/testA")
    public String testA(){
        //测试线程阈值
        try {
            TimeUnit.MILLISECONDS.sleep(800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "testA-----";
    }

当你连续点击看到如下限流提示(1s请求数量超过配置阈值)
在这里插入图片描述

5.3测试关联限流

配置如下
在这里插入图片描述
用postman模拟与/testA关联的/testB到达阈值
在这里插入图片描述
postman启动之后调用/testA可以看到该接口被限流。

5.4测试Warm up(限流冷启动)

配置如下(访问流量忽然增大时,从阈值/冷加载因子 开始经过预热时长达到每秒可访问阈值,即从流量增大时刚开始可以支持每秒2次访问,经过3s可以支持每秒6次访问接口),默认冷加载因子(coldFactor)为3
在这里插入图片描述
可以点击访问/testA接口测试,开始慢慢慢点击没问题,然后加快速度会出现限流,过一会之后,限流就没有了。

5.5.排队等候

配置如下
在这里插入图片描述
为了方便查看调用,接口中打印线程名称

@GetMapping("/testB")
    public String testB(){
        log.info(Thread.currentThread().getName() + "...testB ");
        return "testB   -----";
    }

重启项目调用,可以在控制台中看到调用记录
在这里插入图片描述

6.测试降级规则

官方文档

  • RT:
    • 当资源的平均响应时间超过阈值(这里阈值即自己配置的毫秒值)且在时间窗口内通过的请求>= 5,两个条件同时满足触发熔断降级
    • 窗口期后关闭断路器。
    • RT最大4900(更大通过 -Dcsp.sentinel.statistic.max.rt=xxx 来配置)
  • 异常比例:
    QPS>=5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级。
  • 异常数:
    异常数(分钟统计)超过阈值,触发降级;时间窗口结束后,关闭降级

Sentienl熔断降级会在调用调用链路中某个资源出现不稳定时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误。
当资源降级后,在接下来的降级时间窗口之内,对该资源的调用自动熔断(默认行为是抛出DegradeException)


6.1测试RT

1.添加测试接口

@GetMapping("/testD")
    public String testD(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("testD 测试RT");
        return "testD -----";
    }

为了让平均响应时间增大,在代码中加入sleep休眠。
2.利用JMater进行压力测试
1s 10个线程访问/testD接口
在这里插入图片描述
在这里插入图片描述
3.配置如下
在这里插入图片描述
4.启动JMeter,自己调用/testD,可以看到该接口已被熔断降级。
在这里插入图片描述
熔断分析:1.平均响应时间(1000ms)超出阈值(200ms) 2.在时间窗口内通过请求10*5=50>=5。满足两个条件,所以发生熔断降级

6.2测试异常比例

资源每秒请求量>=5&&每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的时间窗口之内,对这个方法的调用都会自动的返回(服务熔断)。异常比率阈值范围[0.0,1.0],代表0%-100%。

1.提供测试异常比例接口

@GetMapping("/testException")
    public String testException(){
        log.info("testException 异常比例");
        int age = 10 /0 ;
        return "testException -----";
    }

2.利用JMater进行压力测试,除接口外其他配置同上面测试RT
在这里插入图片描述
3.sentinel配置
在这里插入图片描述
4.重启项目并启动JMeter进行测试,调用/testException可以服务熔断提示信息
在这里插入图片描述
在这里插入图片描述

6.3测试异常数

当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若时间窗口小于60s,则结束熔断状态后可能再进入熔断状态。

1.提供测试接口

 @GetMapping("/testExceptionCount")
    public String testExceptionCount(){
        log.info("testExceptionCount 异常数");
        int age = 10 /0 ;
        return "testExceptionCount -----";
    }

2.sentinel配置
在这里插入图片描述
3.重启项目进行测试
前面5次访问都报错,后面访问进入熔断后降级。
在这里插入图片描述
熔断后降级如下图
在这里插入图片描述

7.热点参数限流

在这里插入图片描述

7.1普通热点参数限流

1.提供接口进行测试

	@GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey", blockHandler = "dealTestHotKey")
    public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                             @RequestParam(value = "p2", required = false) String p2){
        return "testHotKey -----";
    }

    public String dealTestHotKey(String p1, String p2, BlockException blockException){
        return "dealTestHotKey---------";
    }

2.sentinel配置
在这里插入图片描述
上面的配置第一个参数p1,当QPS超过1秒1次点击后马上被限流。
3.重启项目测试
在这里插入图片描述

7.2参数例外项热点限流

1.sentinel配置如下
在这里插入图片描述
以上配置含义,当第一个参数p1的值为5时接口/testHotKey的流量阈值为200

注意:

@SentinelResource处理的是控制台配置的违规情况,有blockHandler方法配置的兜底处理。
但是@SentinelResource不管代码中出现的运行时期异常(RuntimeException)

在这里插入图片描述

8.@SentinelResource配置

8.1按资源名称或URL地址限流加后续处理

1.提供接口

    /**
     * (违反sentinel配置)手动配置兜底处理blockHandler
     * @return
     */
    @GetMapping(value = "/byResource")
    @SentinelResource(value = "byResource", blockHandler = "handleException")
    public CommonResult byResource(){
        return new CommonResult(200, "按资源名称限流测试OK");
    }

    public CommonResult handleException(BlockException blockException){
        return new CommonResult<>(444, blockException.getClass().getCanonicalName()+"\t服务不可用" );
    }


    /**
     * 默认处理
     * @return
     */
    @GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl")
    public CommonResult byUrl(){
        return new CommonResult(200, "by url限流测试OK");
    }
 

2.sentinel配置
资源名称:
在这里插入图片描述
url:
在这里插入图片描述

3.重新启动测试
访问byResource当每秒访问超过1次时,使用自定义的兜底处理。
在这里插入图片描述
访问/rateLimit/byUrl当每秒访问超过1次时,使用默认处理。
在这里插入图片描述


以上配置存在问题:

  • 1.系统默认的,没有体现我们自己的业务需求
  • 2.依照现有条件,我们自定义的处理方法和业务代码耦合在一块,不直观
  • 3.每个业务方法都有一个兜底的,代码膨胀加剧
  • 4.全局同一处理方法没有体现

8.1解决存在的问题

1.提供接口

public class CustomerBlockHandler {

    public static CommonResult handlerException(BlockException exception) {
        return new CommonResult(444, "客户自定义,global handlerException---1");
    }

    public static CommonResult handlerException2(BlockException exception) {
        return new CommonResult(444, "客户自定义,global handlerException---2");
    }
}

 @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
                    blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
    public CommonResult customerBlockHandler(){
        return new CommonResult(200, "客户自定义 限流测试OK");
    }

2.sentinel中配置
在这里插入图片描述
3.重启测试

9.整合Ribbon和OpenFeign

9.1准备

1.创建三个springboot项目,分别为alibaba-consumer2、alibaba-provider3、alibaba-provider4(alibaba-provider3/alibaba-provider4一样)
由于东西过多这里不罗列,详细参考https://github.com/xiaoxiaoshou/springclouddemo

9.2Ribbon系列

主要示例代码:

@RestController
@Slf4j
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public class CircleBreakerController {
    private 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的,fallback只负责业务异常
//    @SentinelResource(value = "fallback",blockHandler = "blockHandler") // 配置了blockHandler,只负责sentinel控制台配置违规
   @SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler") // 配置了blockHandler和fallback
 //   @SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler", exceptionsToIgnore = {IllegalArgumentException.class}) // 忽略运行时IllegalArgumentException异常不进行自定义处理
    public CommonResult<Payment> fallback(@PathVariable("id") Long id){
        CommonResult<Payment> commonResult = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
        if(id == 4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常");
        }else if(commonResult.getData() == null){
            throw new NullPointerException("NullPointerException,该ID没有记录,空指针异常");
        }
        return commonResult;
    }
    // 本例是fallback
    public CommonResult handlerFallback(Long id, Throwable e){
        Payment payment = new Payment(id, null);
        return new CommonResult(444, "兜底异常handler,exception内容"+e.getMessage(), payment);
    }

    public CommonResult blockHandler(Long id, BlockException exception){
        Payment payment = new Payment(id, null);
        return new CommonResult<>(445, "blockHandler-sentinel 限流,无此流水号:blockException" + exception.getMessage(), payment);
    }
}
  • fallback对应方法(handlerFallback方法)处理代码运行时期异常
  • blockHandler对应方法(blockHandler)处理Sentinel中违规
  • exceptionsToIgnore忽略运行时期某个异常不进行自定义处理

配置Sentinel(对资源fallback阈值为1)并启动项目测试,可以看到既可以处理调用过程中对应Sentinel违规也可以处理运行时期异常。

9.3 OpenFeign系列

1.主要配置

#激活sentinel对feign的支持
feign:
  sentinel:
    enabled: true

2.绑定对应服务

@FeignClient(value = "nacos-payment-provider", fallback = PaymentFallbackService.class)
public interface PaymentService {

    @GetMapping("/paymentSQL/{id}")
    CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

@Component
public class PaymentFallbackService implements PaymentService {
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
        return new CommonResult<>(444, "fallback");
    }
}

3.提供访问接口

    @Resource
    private PaymentService paymentService;

    @GetMapping("/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        return paymentService.paymentSQL(id);
    }

4.重启项目即可冲过consumer接口调用第三方接口


各熔断器的比较:
在这里插入图片描述

10.规则持久化

当你每次重启对应服务,你会发现在Sentinel中配置对应的规则就没有了,在生产环境中我们需要配置规则的持久化(持久化工具都可以,官方推荐Nacos)。

1.引入依赖

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

2.配置文件配置

spring.cloud.sentinel.datasource.dsl.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.dsl.nacos.data-id=${spring.application.name}
spring.cloud.sentinel.datasource.dsl.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.dsl.nacos.data-type=json
spring.cloud.sentinel.datasource.dsl.nacos.rule-type=flow

3.在nacos中配置

[
	{
	"resource":"/rateLimit/byUrl",
	"limitApp":"default",
	"grade":1,
	"count":1,
	"strategy":0,
	"controlBehavior":0,
	"clusterMode":false
	}
]

在这里插入图片描述
配置项含义:

  • resource:资源名称
  • limitApp:来源应用
  • grade:阈值类型,0表示线程数,1表示QPS
  • count:单机阈值
  • strategy:流控模式,0表示直接,1表示级联,2表示链路
  • controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等候
  • clusterMode:是否集群

猜你喜欢

转载自blog.csdn.net/qq_41262903/article/details/107107513