Sentinel

     下载sentinel-dashboard-1.8.3.jar,通过命令行启动

--server.port:自定义服务器端口。默认为 8080 端口。
--auth.username 和 --auth.password:自定义账号和密码。默认为「sentinel / sentinel」。
--logging.file:自定义日志文件。默认为 ${user.home}/logs/csp/sentinel-dashboard.log。
 java -jar sentinel-dashboard-1.8.1.jar --server.port=8080

添加依赖

 <!-- Sentinel 核心库 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.3</version>
        </dependency>
        <!-- Sentinel 接入控制台 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.8.3</version>
        </dependency>
        <!-- Sentinel 对 SpringMVC 的支持 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-webmvc-adapter</artifactId>
            <version>1.8.3</version>
        </dependency>
        <!-- Sentinel 对 Spring AOP 的拓展 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
            <version>1.8.3</version>
        </dependency>

添加Sentinel拦截器

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        addSentinelWebTotalInterceptor(registry);
        addSentinelWebInterceptor(registry);
    }

    private void addSentinelWebInterceptor(InterceptorRegistry registry) {
        SentinelWebMvcConfig config = new SentinelWebMvcConfig();
        /**
         * 是否包含请求方法,即基于URL创建资源,是否包含Method
         */
        config.setHttpMethodSpecify(true);
        /**
         * 设置BlockException处理器,对达到流量控制阈值后的请求处理
         */
        config.setBlockExceptionHandler(new MyBlockExceptionHandler());
        /**
         * 添加SentinelWebInterceptor拦截器
         */
        registry.addInterceptor(new SentinelWebInterceptor(config))
                .addPathPatterns("/**");
    }

    private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) {
        /**
         * 针对全局URL进行流量控制,将所有URL合计流量,全局统一控制
         */
        SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();
        registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**");
    }
}

Sentinel在发生blockException时,默认返回仅仅是一句Blocked by Sentinel (flow limiting)而大部分的应用一般都会统一返回一个固定json格式的数据

BlockException 是一个异常抽象基类,其有 5 个实现类,对应 Sentinel 的 5 种流量控制手段,如下图所示:

 BlockException异常默认处理类

     在 SentinelWebInterceptor 拦截器中,满足配置的 Sentinel block 的条件时,Sentinel 会抛出 BlockException 异常。定义 BlockExceptionHandler 接口的实现类,可以实现对 BlockException 的异常处理

  默认情况下,BlockExceptionHandler 有一个默认的 DefaultBlockExceptionHandler 实现类,返回 Block 字符串提示。这个类是默认处理异常阻塞的,代码如下:

//Default handler for the blocked request
public class DefaultBlockExceptionHandler implements BlockExceptionHandler {
​
  @Override
  public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
    // ... 省略其它代码
​
    PrintWriter out = response.getWriter();
    out.print("Blocked by Sentinel (flow limiting)");
  }
​
}

自定义BlockException异常处理

      通过实现BlockExceptionHandler接口来自定义BlockException异常处理

       fallback 和 blockHandler 的差异点在于, blockHandler 只能处理 BlockException 异常,fallback 能够处理所有异常。如果都配置的情况下,BlockException 异常分配给 blockHandler 处理,其它异常分配给 fallback 处理。

@Slf4j
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        Result result = null;

        if (e instanceof FlowException) {
            result = Result.fail(5001, "接口限流");
        } else if (e instanceof DegradeException) {
            result = Result.fail(5002, "服务降级");
        } else if (e instanceof SystemBlockException) {
            result = Result.fail(5003, "系统触发保护");
        } else if (e instanceof AuthorityException) {
            result = Result.fail(5004, "权限规则失败");
        }
        httpServletResponse.setStatus(HttpStatus.OK.value());
        httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.getWriter().write(JSONObject.toJSONString(result));
    }
}

     添加sentinel.properties启动配置项,指定接入控制台

csp.sentinel.dashboard.server=127.0.0.1:8080

启动类

@SpringBootApplication
public class BootSentinelApplication {

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

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

}

测试类

@RestController
@RequestMapping("/foo")
public class FooController {

    @Autowired
    private RestTemplate restTemplate;

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

    @GetMapping("/sleep")
    public String sleep() throws InterruptedException {
        Thread.sleep(200L);
        return "sleep";
    }
}

控制台效果

流量控制(flow control)

       监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。Sentinel在限流时会抛出BlockException的子类FlowException

  

  • 资源名(resource):限流规则的作用对象
  • 针对来源(limitApp):流控针对的调用来源,为 default 则不区分调用来源。通过ContextUtil.enter(resourceName, origin) 方法中的 origin 参数标明调用方身份。当参数为{some_origin_name}时表示只有来自这个调用者的请求才会进行流量控制。other则表示除{some_origin_name} 以外的其余调用方的流量进行流量控制。
  • 阈值类型(grade):QPS/并发数
    并发数控制用于保护业务线程池不被慢调用耗尽。Sentinel 并发控制不负责创建和管理线程池,简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。推荐使用QPS。
  • 流控模式(strategy):调用关系限流策略,包括直接(STRATEGY_DIRECT)、关联(STRATEGY_RELATE)和链路(STRATEGY_CHAIN)
    直接是limitApp中指定的origin

          关联是具有竞争或依赖关系的资源,如配置写库来达到优写的目的;使用场景:比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是有限支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。

          

          当/update资源访问量触发阈值时,就会对/query资源限流,避免影响/update资源

          链路指同资源下被指定的入口将被监控限流,而且其它入口则直接放行,例如有两条请求链路:

  • /test1 --> /common
  • /test2  --> /common

          如果只希望统计从/test2进入到/common的请求,则可以这样配置: 

  • 流控效果(controlBehavior):由QPS限流触发,直接拒绝、Warm Up、匀速排队
    直接拒绝是在打到QPS阈值时,直接抛出FlowException异常,适用于测压后确定出系统处理能力的准水位。
    Warm UP为预热/冷启动,是应对服务冷启动的一种方案。请求阈值初始值是 threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。而coldFactor的默认值是3. 例如,我设置QPS的threshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3,然后在5秒后逐渐增长到10.
  • 排队等待是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。

    例如:QPS = 5,意味着每200ms处理一个队列中的请求;timeout = 2000,意味着预期等待超过2000ms的请求会被拒绝并抛出异常

熔断降级

  Sentinel熔断策略包括:慢调用比例(默认最大RT=4900ms)、异常比例、异常数

        慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。例如:

  RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。

       异常比例:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值,则触发熔断。

  异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例超过指定异常数,则触发熔断。

  当【统计时长】周期内,发起的请求数大于【最小请求数】时,会根据【最大RT/比例阈值/异常数】进行熔断,窗口期为【熔断时长】

  

热点参数限流 

       根据请求的参数来做限流,比如请求带的参数是热点参数,就对这个请求应用特殊的限流规则;对携带非热点参数的请求,走另一个限流规则

       

  • 资源名:@SentinelResource(value = "xxx") 中的 value 值;
  • 参数索引(0):针对第 0 个参数;
  • 参数类型(long):索引位置是 0 的参数的类型是 long;
  • 参数值(1):如果其值是 1;
  • 限流阈值(1):允许的 QPS 就是 1;
  • 单机阈值(10):不是 1 的情况下,允许的 QPS 就是 10;
  • 统计时间窗口(1):统计的时间单位,一般都是 1s;

系统自适应限流参数

       

  • LOAD:只有在 Linux 系统的机器上才会生效,可以根据当前操作系统的负载,来决定是否触发保护(把请求拒绝掉);
  • RT:这个应用上,所有请求的平均响应时间,如果超过某个值,就停止新的请求;
  • 线程数:这个应用上,所有的请求消耗的线程数加起来,如果超过某个值,就停止新的请求;
  • 入口 QPS:这个应用上,所有接口的 QPS 加起来,如果超过某个值,就停止新的请求;
  • CPU 使用率:CPU 的使用率,如果超过一个百分比,就停止新的请求;
posted on 2022-04-04 17:41  溪水静幽  阅读(274)  评论(0)    收藏  举报