SpringCloudAlibaba-熔断_限流(Sentinel)

Sentinel

概述

https://github.com/alibaba/sentinel

https://sentinelguard.io/zh-cn/docs/introduction.html

https://spring-cloud-alibaba-group.github.io/github-pages/2021/en-us/index.html#_spring_cloud_alibaba_sentinel

有Hystrix,为什么需要Sentinel?

Hystrix

1、Hystrix需要自己搭建监控平台

2、Hystrix没有一套web界面给我们进行 更细粒度化的配置(流量监控、速率监控、服务熔断、服务降级...)

Sentinel

1、单独的组件

2、界面化的细粒度配置

what

1、Sentinel 是面向分布式、多语言异构化服务架构的 流量治理组件

  主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性;

Sentinel基本概念

资源

  资源是 Sentinel 的关键概念;

  可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码;

  只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来;

  大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源;

规则

  围绕资源的实时状态设定的规则;

  可以包括流量控制规则、熔断降级规则以及系统保护规则;

  所有规则可以动态实时调整;

How

安装Sentinel控制台

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

  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持(见 主流框架适配)。
  • 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。

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

运行前提 

jdk8以上环境、8080端口不能被占用

java -jar xxxx.jar

访问 http://localhost:8080/#/login

  默认 账号/密码 sentinel/sentinel

创建8401微服务 且被 Sentinel监控

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



server:
  port: 8401

spring:
  application:
    name: sentinel
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080   #sentinel dashboard地址
        port: 8719  #默认端口,如被占用,从8719+1开始,直至找到未被使用的端口

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


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


@RestController
public class FlowLimitController {

    @GetMapping(value = "/testA")
    public String testA(){
        return "testA";
    }

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

}

  

Sentinel采用懒加载,只有访问服务,在Sentinel dashboard才能看到;

流控规则(流量控制)

https://sentinelguard.io/zh-cn/docs/flow-control.html

概念

阈值类型 

QPS

  每秒请求数;

  当QPS达到阈值,进行限流;

线程数

  支持实际处理任务的线程数;

  当实际请求的线程数 > 支持的线程数,进行限流; 

流控模式

直接(默认)

  直接 -> 快速失败 (系统默认) 

  /testB达到阈值,进行限流

 

关联

  testA达到阈值,限流testB

流控效果

快速失败

  直接抛异常 

warm up

  阈值/coldFactor(默认3),经过预热时长,达到阈值

  10/3 经过5秒 达到阈值10

排队等待 

  匀速排队;

  严格控制请求通过的时间间隔; 

  对应 漏桶算法;  

 

   每秒均匀处理一个请求

 

降级规则 

https://sentinelguard.io/zh-cn/docs/circuit-breaking.html

概念

 

Sentinel的断路器没有 半开状态;

How

RT

  testC 必须在200ms内处理响应,否则开启熔断,经过2s窗口期,关闭熔断 

 

异常比例

  testC 异常比例>0.2,开启熔断,经过2s窗口期,关闭熔断;  

 

异常数

 

  testC 单位时间内异常次数达到3次,开启熔断,经过61s窗口期,关闭熔断;

 

热点Key限流 

https://sentinelguard.io/zh-cn/docs/parameter-flow-control.html

@SentinelResource的blockHandler  仅处理后台配置出现的异常;

How

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

    public String handle_testD(String p1,String p2, BlockException blockException){
        return "o(╥﹏╥)o handle_testD";
    }

  http://localhost:8401/testD?p1=a           当p1参数达到阈值,开启熔断

  http://localhost:8401/testD?p1=a&p2=b      当p1参数达到阈值,开启熔断

  http://localhost:8401/testD?p2=b     无论是否达到阈值,都不会开启熔断 

 

 

  testDResource 资源下标为0的参数 QPS>1 时,熔断开启,服务降级处理handle_testD,经过1s窗口期,熔断关闭;

参数例外项 

  当某个参数值是指定值时,期望该参数的阈值可以提升至某个值;

 

  testDResource 资源下标为0的参数 QPS>1 时,熔断开启;

    当 下标为0的参数值=5,QPS<=200; 

系统规则 

https://sentinelguard.io/zh-cn/docs/system-adaptive-protection.html

阈值类型

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。
  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

 

  谨慎使用

 

@SentinelResource

资源名称限流 + blockHandle

@GetMapping(value = "/byResource")
    @SentinelResource(value = "byResourceResource", blockHandler = "handle_byResource")
    public String testE(@RequestParam(value = "p1", required = false) String p1){
        return "---testD";
    }

    public String handle_byResource(String p1, BlockException blockException){
        return "o(╥﹏╥)o handle_byResource" + blockException.getClass().getCanonicalName();
    }

  

  byResourceResource QPS>2时开启熔断,handle_byResource进行熔断处理;

 

URL限流 + blockHandle

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

  

 

  /byURL QPS>1时,开启熔断;

 

目前存在的问题  

1、系统默认的,没有体现业务要求

2、自定义处理 与 业务代码耦合

3、每个业务方法都要建一个 处理方法,代码臃肿

4、全局统一的处理没有体现

自定义BlockHandler 

public class MyBlockHandler {

    public static String handler1(BlockException blockException){
        return "---handler1 o(╥﹏╥)o";
    }


    public static String handler2(String p1, BlockException blockException){
        return "---handler2 o(╥﹏╥)o";
    }
}

  

@GetMapping(value = "/myBlockHandle")
    @SentinelResource(value = "myBlockHandleResource" ,blockHandlerClass = MyBlockHandler.class, blockHandler = "handler2")
    public String myBlockHandle(@RequestParam(value = "p1", required = false) String p1){
        return "---myBlockHandle";
    }

  

 

@SentinelResource详解

https://sentinelguard.io/zh-cn/docs/annotation-support.html

 

服务熔断

nacos+ribbon+sentinel

provider

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


server:
  port: 9001

spring:
  application:
    name: nacos-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

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


@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderDemoApplication9001 {

    public static void main(String[] args) {
        SpringApplication.run(NacosProviderDemoApplication9001.class, args);
    }
}


@RestController
public class EchoController {

    @Value(value = "${server.port}")
    private String port;

    @GetMapping(value = "/echo/{string}")
    public String echo(@PathVariable String string) {
        return "Hello Nacos Discovery " + string + "port:" + port;
    }
}

  

consumer

<!--    spring-cloud-starter-alibaba-nacos-discovery 集成了ribbon    -->
        <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>



server:
  port: 84

spring:
  application:
    name: nacos-ribbon-cosumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080   #sentinel dashboard地址
        port: 8719  #默认端口,如被占用,从8719+1开始,直至找到未被使用的端口

service-url:  #消费者要调用的服务名称
  nacos-user-service: http://nacos-provider


@Configuration
public class Config {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}


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

}


@RestController
public class Controller {

    @Value(value = "${service-url.nacos-user-service}")
    private String serviceUrl;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "/nacos/consumer/{s}")
    @SentinelResource(
            value = "test",
            fallback = "handleTestFallback",    //fallback负责运行时异常
            blockHandler = "handleTestBlockHandler",    //blockHandler负责sentinel平台配置规则
            exceptionsToIgnore = {NullPointerException.class}   //指定异常将不会被 fallback或blockHandler
    )
    public String test(@PathVariable(value = "s") String s){

        if (s.equalsIgnoreCase("a")){
            throw new IllegalArgumentException("IllegalArgumentException...");
        }
        else if (s.equalsIgnoreCase("b")){
            throw new NullPointerException("NullPointerException...");
        }

        return restTemplate.getForObject(serviceUrl + "/echo/"+s, String.class);
    }

    public String handleTestFallback(String s, Throwable e){
        return "handleTestFallback...s="+s + "msg="+e.getMessage();
    }

    public String handleTestBlockHandler(String s, BlockException e){
        return "handleTestBlockHandler...s="+s + "msg="+e.getMessage();
    }
}

  

nacos+openfeign+sentinel  

provider

如上

consumer

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

        <!--    spring-cloud-starter-alibaba-nacos-discovery 集成了ribbon    -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

    </dependencies>


server:
  port: 85

spring:
  application:
    name: nacos-ribbon-cosumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080   #sentinel dashboard地址
        port: 8719  #默认端口,如被占用,从8719+1开始,直至找到未被使用的端口

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



@FeignClient(value = "nacos-provider", fallback = MyFeignClientFallBack.class)
public interface MyFeignClient {

    @GetMapping(value = "/echo/{s}")
    String test(@PathVariable(value = "s") String s);

}



@Component
public class MyFeignClientFallBack implements MyFeignClient{
    public String test(String s) {
        return "myFeignClientFallBack...";
    }
}


@RestController
public class Controller {

    @Autowired
    private MyFeignClient myFeignClient;

    @GetMapping(value = "/nacos/consumer/{s}")
    @SentinelResource(
            value = "test",
            fallback = "handleTestFallback",    //fallback负责运行时异常
            blockHandler = "handleTestBlockHandler",    //blockHandler负责sentinel平台配置规则
            exceptionsToIgnore = {NullPointerException.class}   //指定异常将不会被 fallback或blockHandler
    )
    public String test(@PathVariable(value = "s") String s){

        if (s.equalsIgnoreCase("a")){
            throw new IllegalArgumentException("IllegalArgumentException...");
        }
        else if (s.equalsIgnoreCase("b")){
            throw new NullPointerException("NullPointerException...");
        }

        return myFeignClient.test(s);
    }

    public String handleTestFallback(String s, Throwable e){
        return "handleTestFallback...s="+s + "msg="+e.getMessage();
    }

    public String handleTestBlockHandler(String s, BlockException e){
        return "handleTestBlockHandler...s="+s + "msg="+e.getMessage();
    }
}

  

熔断框架对比 

 

sentinel规则持久化

why

应用重启后,sentinel配置的规则将会全部消失;

How

将sentinel配置的规则持久化到nacos

<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>
<!--        sentinel规则持久化到nacos-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>



server:
  port: 8401

spring:
  application:
    name: sentinel
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080   #sentinel dashboard地址
        port: 8719  #默认端口,如被占用,从8719+1开始,直至找到未被使用的端口
      datasource:     #sentinel规则持久化到nacos数据库配置
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}  #应用名称
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

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



@GetMapping(value = "/byResource")
    public String testE(@RequestParam(value = "p1", required = false) String p1){
        return "---testD";
    }



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

  

  

nacos-server后台规则配置

 

posted on 2022-08-22 15:30  anpeiyong  阅读(92)  评论(0编辑  收藏  举报

导航