美文网首页K8sSpring Cloud
Spring Cloud Alibaba Sentinel组件

Spring Cloud Alibaba Sentinel组件

作者: 小波同学 | 来源:发表于2019-11-20 01:16 被阅读0次

    什么是服务雪崩效应

    服务雪崩效应是一种因“服务提供者服务的不可用”(原因)导致“服务调用者服务不可用”(结果),并将不可用逐渐放大的现象。如下图所示


    image.png

    形成原因

    服务雪崩的过程可以分为三个阶段:

    • 服务提供者不可用;
    • 重试加大请求流量;
    • 服务调用者不可用;
      服务雪崩的每个阶段都可能由不同的原因造成,总结如下:


      image.png

    应对策略

    常见容错方案:

    1、超时
    2、限流
    3、舱壁模式(如每个controller都有自己独立的线程池,之间互不干扰)
    4、断路器模式

    image.png

    全面应对策略:

    image.png

    Sentinel 是什么

    随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助您保护服务的稳定性。

    大家可能会问:Sentinel 和之前常用的熔断降级库 Netflix Hystrix 有什么异同呢?Sentinel官网有一个对比的文章,这里摘抄一个总结的表格,具体的对比可以点此 链接 查看。

    对比内容 Sentinel Hystrix
    隔离策略 信号量隔离 线程池隔离/信号量隔离
    熔断降级策略 基于响应时间或失败比率 基于失败比率
    实时指标实现 滑动窗口 滑动窗口(基于 RxJava)
    规则配置 支持多种数据源 支持多种数据源
    扩展性 多个扩展点 插件的形式
    基于注解的支持 支持 支持
    限流 基于 QPS,支持基于调用关系的限流 不支持
    流量整形 支持慢启动、匀速器模式 不支持
    系统负载保护 支持 不支持
    控制台 开箱即用,可配置规则、查看秒级监控、机器发现等 不完善
    常见框架的适配 Servlet、Spring Cloud、Dubbo、gRPC 等 Servlet、Spring Cloud Netflix

    从对比的表格可以看到,Sentinel比Hystrix在功能性上还要强大一些,本文让我们一起来了解下Sentinel的源码,揭开Sentinel的神秘面纱。

    Sentinel功能特点

    1、丰富的应用场景:例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等
    2、完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
    3、广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
    4、完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

    image.png

    开源生态

    image.png

    Sentinel 分为两个部分:

    核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
    控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

    为应用整合Sentinel

    引入maven依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <dependencies>
        <!--sentinel整合之后会暴露出/actuator/sentinel-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <!--整合Spring Cloud-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--整合Spring Cloud Alibaba-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>0.9.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    

    加入配置暴露/actuator/sentinel端点

    #添加sentinel依赖后 暴露/actuator/sentinel端点
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    

    启动服务访问http://localhost:port/actuator/sentinel会返回json信息,说明已经整合好了Sentinel

    搭建Sentinel控制台

    下载Sentinel控制台:https://github.com/alibaba/Sentinel/releases

    为服务整合Sentinel控制台:

    spring:
        cloud:
            sentinel:
            filter:
                #打开/关闭掉对Spring MVC端点的保护
                enabled: true
            transport:
                port: 8719
                #指定sentinel控制台的地址
                dashboard: localhost:8080
    
    image.png

    定义资源:也就是对哪个资源进行流量控制,现在已经提供了注解形式,所以新的接入直接用注解,@SentinelResource

    • 关于SentinelResource注解,这里列出几个好用和必填的参数,具体参考这里

    Sentinel控制台配置流控规则

    流控模式

    • 直接:当QPS超过阈值就进行限流。
    • 关联:当关联的资源达到阈值,就限流自己。
      • 适用场景:查询和修改同一表的数据,如果是高并发的应用,查询接口的流量过大,就会影响修改接口的性能,反之同理,这就可以根据业务需求,去衡量希望优先读还是优先写。
      • 关联其实是一种保护关联资源的设计。
    • 链路:只记录指定链路上的流量,即指定资源从入口资源进来的流量如果达到阈值就限流。
      • 链路其实是一种细粒度的针对来源,而编辑流控规则中的针对来源输入框是微服务级别的,可以指定指定微服务过来的流量达到阈值就限流。
      • 而链路是api级别的,指定的是api的调用流量达到阈值就限流。
    流控模式-关联 流控模式-链路

    流控效果

    • 快速失败:直接失败,抛异常
      相关源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
    • Warm Up(预热):根据codeFactor(冷加载因子,默认值为3),从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
      即如果阈值为100,冷加载因子为3,预热时长为10秒,那么就会用100 / 3作为最初的阈值,经过10秒之后才会将阈值达到100,进而进行限流,意思就是让允许通过的流量缓慢增加,在达到一定的时间之后才达到阈值这样会更好的保护微服务
    • 排队等待:匀速排队,让请求以均匀的速度通过,阈值类型必须设置成QPS,否则无效。此种模式可适用于应对突发流量的场景
      相关源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
    image.png 流控效果-排队等待

    Sentinel控制台配置降级规则,即断路器模式,Sentinel目前只有断路器三态中的打开和关闭,没有半开状态

    降级策略

    • RT:平均响应时间
      注意:Sentinel默认RT最大时间为4900毫秒,可通过-Dcsp.sentinel.statistic.max.rt=xxx修改


      image.png
    • 异常比例


      image.png
    • 异常数
      注意:时间窗口<60秒可能会出问题
      相关源码:com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule


      image.png
      image.png

    Sentinel控制台配置热点规则,是一种特殊的流控规则,支持对特定的参数和参数的值限流

    适用于存在热点参数(某些参数QPS很高),并希望提升API可用性的场景
    注意:参数必须是基本类型或者String
    相关源码:com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker#passCheck()
    加入代码

    @GetMapping("/test-hot")
    @SentinelResource("hot")
    public String testHot(@RequestParam(required = false) String a,
                          @RequestParam(required = false)String b){
        return a + ":" + b;
    }
    
    image.png

    Sentinel控制台配置系统规则

    阈值类型

    • LOAD
      当系统Load1(1分钟的load)超过阈值,且并发线程数超过系统容量时触发,建议配置为CPU核心数*2.5。(仅对Linux/Unix-like 机器生效)
      系统容量 = maxQps * minRt
      maxQps:秒级统计出来的最大QPS
      minRt:秒级统计出来的最小响应时间
      相关源码:com.alibaba.csp.sentinel.slots.system.SystemRuleManager#checkBbr()
    • RT:所有入口流量的平均RT达到阈值触发
    • 线程数:所有入口流量的并发线程数达到阈值触发
    • 入口QPS:所有入口流量的QPS达到阈值触发
      相关源码:com.alibaba.csp.sentinel.slots.system.SystemRuleManager#checkSystem()


      image.png

    Sentinel控制台配置授权规则

    • 白名单:资源名里的资源只允许为白名单里面的流控应用访问。
    • 黑名单:资源名里的资源不允许为黑名单里面的流控应用访问。
    • 授权规则通过调用来源从而实现对服务消费者的授权或者限制。
    image.png

    Sentinel使用Java代码方式配置规则

    请点击:
    Alibaba Sentinel 规则参数总结
    Alibaba Sentinel 配置项总结

    Sentinel与控制台通信原理剖析

    image.png

    微服务注册到Sentinel控制台和发送心跳源码:com.alibaba.csp.sentinel.transport.heartbeat.SimpleHttpHeartbeatSender
    微服务和Sentinel控制台通信API源码:com.alibaba.csp.sentinel.command.CommandHandler接口的实现类

    修改控制台规则是如何通知客户端的?
    看sentinel-transport-simple-http包中的HttpEventTask类,它开启了一个线程,专门用来做为socket连接,控制台通过socket请求通知客户端,从而更新客户端规则,更改规则核心代码如下:

    long start = System.currentTimeMillis();
    in = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), SentinelConfig.charset()));
    OutputStream outputStream = this.socket.getOutputStream();
    printWriter = new PrintWriter(new OutputStreamWriter(outputStream, Charset.forName(SentinelConfig.charset())));
    String line = in.readLine();
    CommandCenterLog.info("[SimpleHttpCommandCenter] socket income: " + line + "," + this.socket.getInetAddress(), new Object[0]);
    CommandRequest request = this.parseRequest(line);
    String commandName = HttpCommandUtils.getTarget(request);
    if (!StringUtil.isBlank(commandName)) {
        CommandHandler<?> commandHandler = SimpleHttpCommandCenter.getHandler(commandName);
        if (commandHandler != null) {
            CommandResponse<?> response = commandHandler.handle(request);
            this.handleResponse(response, printWriter, outputStream);
        } else {
            this.badRequest(printWriter, "Unknown command `" + commandName + '`');
        }
    
        printWriter.flush();
        long cost = System.currentTimeMillis() - start;
        CommandCenterLog.info("[SimpleHttpCommandCenter] Deal a socket task: " + line + ", address: " + this.socket.getInetAddress() + ", time cost: " + cost + " ms", new Object[0]);
        return;
    }
    
    this.badRequest(printWriter, "Invalid command");
    

    Sentinel配置项

    微服务应用端连接Sentinel控制台配置项

    spring.cloud.sentinel.transport:
        # 指定控制台的地址
        dashboard:localhost:8080
        # 指定和控制台通信的IP
        # 如不配置,会自动选择一个IP注册
        client-ip:127.0.0.1
        # 指定和控制台通信的端口,默认8719
        # 如不设置,会自动从8719开始扫描,依次+1,直到找到未被占用的端口
        port:8719
        # 心跳发送周期,默认值null
        # 但在SimpleHttpHeartbeatSender会用默认值10秒
        heartbeat-interval-ms:1000
    

    Sentinel控制台配置项

    配置项 默认值 最小值 描述
    sentinel.dashboard.app.hideAppNoMachineMillis 0 60000 是否隐藏无健康节点的应用,距离最近
    一次主机心跳时间的毫秒数,默认关闭
    sentinel.dashboard.removeAppNoMachineMillis 0 120000 是否自动删除无健康节点的应用,距离
    最近一次其下节点心跳时间毫秒数,默
    认关闭
    sentinel.dashboard.unhealthyMachineMillis 60000 30000 主机失联判定,不可关闭
    sentinel.dashboard.autoRemoveMachineMillis 0 300000 距离最近心跳时间超过指定时间是否
    自动删除失联节点,默认关闭
    server.port 8080 - 指定端口
    csp.sentinel.dashboard.server localhost:8080 - 指定地址
    project.name - - 指定程序名称
    sentinel.dashboard.auth.username[1.6] sentinel - dashboard登录账号
    sentinel.dashboard.auth.password[1.6] sentinel - dashboard登录密码
    server.servlet.session.timeout[1.6] 30分钟 - 登录session过期时间 配置为
    7200表示7200秒 配置为60m表示为60分钟

    Sentinel API

    • Sphu:定义资源,让资源受到监控并保护资源。
    • Tracer:可以对我们想要的异常进行统计。
    • ContextUtil:可以实现调用来源,还可以标记调用。
    @GetMapping("/test-sentinel-api")
    public String testSentinelAPI(@RequestParam(required = false) String a){
        String resourceName = "test-sentinel-api";
        ContextUtil.enter(resourceName,"test-wfw");
    
        Entry entry = null;
        try {
            //定义一个sentinel保护的资源 名称是test-sentinel-api
            entry = SphU.entry(resourceName);
            //被保护的业务逻辑
            if(StringUtils.isEmpty(a)){
                throw new IllegalArgumentException("a不能为空");
            }
            return a;
        } catch (BlockException e) {
            //如果被保护的资源被限流或者降级了,就会抛BlockException
            log.warn("限流,或者降级了...",e);
            return "限流,或者降级了...";
        }catch (IllegalArgumentException e) {
            //统计IllegalArgumentException发生的次数,发生的占比等
            Tracer.trace(e);
            return "参数非法...";
        } finally {
            if(entry != null){
                //退出entry
                entry.exit();
            }
            ContextUtil.exit();
        }
    }
    
    流控规则-针对来源

    Sentinel @SentinelResource详解

    @SentinelResource使用方式一

    //使用blockHandler属性,blockHandler的方法必须和资源在同一类中,并且有相同的参数和返回值
    @PostMapping
    @SentinelResource(value = "createOrder",blockHandler = "doOnBlock")
    public OrderInfo create(@RequestBody OrderInfo order, @AuthenticationPrincipal String username){
        log.info("用户名为:username={}",username);
        PriceInfo priceInfo = priceFeignClient.getPrice(order.getProductId());
        log.info("商品价格为,priceInfo={}",priceInfo);
        return order;
    }
    
    public OrderInfo doOnBlock(@RequestBody OrderInfo order, @AuthenticationPrincipal String username, BlockException exception){
        log.info("blocked by :blockException={}",exception.getClass().getSimpleName());
        return order;
    }
    

    @SentinelResource使用方式二

    @PostMapping
    @SentinelResource(value = "createOrder",blockHandler = "doOnBlock",blockHandlerClass = SentinelBlockHandler.class)
    public OrderInfo create(@RequestBody OrderInfo order, @AuthenticationPrincipal String username){
        log.info("用户名为:username={}",username);
        PriceInfo priceInfo = priceFeignClient.getPrice(order.getProductId());
        log.info("商品价格为,priceInfo={}",priceInfo);
        return order;
    }
    
    @Slf4j
    public class SentinelBlockHandler {
    
        public static OrderInfo doOnBlock(@RequestBody OrderInfo order, @AuthenticationPrincipal String username, BlockException exception){
            log.info("blocked by :blockException={}",exception.getClass().getSimpleName());
    
            return order;
        }
    }
    

    @SentinelResource其与属性和新增属性

    属性 作用 是否必须
    value 资源名称
    entryType entry类型,标记流量的方向,取值IN/OUT,默认是OUT
    blockHandler 处理BlockException的函数名称。函数要求:
    1. 必须是 public
    2.返回类型与原方法一致
    3. 参数类型需要和原方法相匹配,并在最后加 BlockException 类型
    的参数。
    4. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置
    blockHandlerClass ,并指定blockHandlerClass里面的方法。
    blockHandlerClass 存放blockHandler的类。对应的处理函数必须static修饰,
    否则无法解析,其他要求:同blockHandler。
    fallback 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对
    所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)
    进行处理。函数要求:
    1. 返回类型与原方法一致
    2. 参数类型需要和原方法相匹配,Sentinel 1.6开始,
    也可在方法最后加 Throwable 类型的参数。
    3.默认需和原方法在同一个类中。若希望使用其他类的函数,
    可配置 fallbackClass ,并指定fallbackClass里面的方法。
    fallbackClass【1.6】 存放fallback的类。对应的处理函数必须static修饰,
    否则无法解析,其他要求:同fallback。
    defaultFallback【1.6】 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的
    异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。
    若同时配置了 fallback 和defaultFallback,以fallback为准。
    函数要求:1. 返回类型与原方法一致
    2. 方法参数列表为空,或者有一个 Throwable 类型的参数。
    3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,
    可配置 fallbackClass ,并指定 fallbackClass 里面的方法。
    exceptionsToIgnore【1.6】 指定排除掉哪些异常。排除的异常不会计入异常统计,
    也不会进入fallback逻辑,而是原样抛出。
    exceptionsToTrace 需要trace的异常 Throwable

    @SentinelResource相关源码

    com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect
    com.alibaba.csp.sentinel.annotation.aspectj.AbstractSentinelAspectSupport

    RestTemplate整合Sentinel

    @Bean
    @LoadBalanced
    @SentinelRestTemplate
    public RestTemplate RestTemplate(){
    
        return new RestTemplate(new HttpComponentsClientHttpRequestFactory());
    }
    
    resttemplate:
      sentinel:
        #false 关闭@SentinelRestTemplate注解,在做开发调试的时候可以关闭此注解,专注于功能的实现
        enabled: true
    

    相关源码:org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor

    Feign整合Sentinel

    feign:
      sentinel:
        #为feign整合Sentinel
        enabled: true
    

    发生限流降级时,自定义处理逻辑

    使用@FeignClient的fallback属性

    @FeignClient(value="priceServer",fallback = PriceFeignClientFallback.class)
    public interface PriceFeignClient {
    
        @GetMapping(value = "/prices/{id}")
        PriceInfo getPrice(@PathVariable("id") Integer id);
    }
    
    /**
     * 发生限流降级时,自定义处理逻辑
     *
     * 一旦PriceFeignClient中远程调用的getPrice()方法被流控了或发生异常了,就会进入此方法
     * 这就相当于一个兜底的行为,保证了服务的可用
     */
    
    @Component
    public class PriceFeignClientFallback implements PriceFeignClient {
    
        @Override
        public PriceInfo getPrice(Integer id) {
            PriceInfo priceInfo = new PriceInfo();
            priceInfo.setId(id);
            priceInfo.setPrice(new BigDecimal(id));
            return priceInfo;
        }
    }
    

    使用@FeignClient的fallbackFactory属性,推荐使用这种方案

    @FeignClient(value="priceServer",fallbackFactory = PriceFeignClientFallbackFactory.class)
    public interface PriceFeignClient {
    
        @GetMapping(value = "/prices/{id}")
        PriceInfo getPrice(@PathVariable("id") Integer id);
    }
    
    /**
     * 发生限流降级时,自定义处理逻辑
     * 一旦PriceFeignClient中远程调用的getPrice()方法被流控了或发生异常了,就会进入此方法
     * 这就相当于一个兜底的行为,保证了服务的可用
     * 相比于FeignClient中的fallback属性而言,fallbackFactory属性在fallback的基础上可以拿到异常信息
     */
    
    @Component
    @Slf4j
    public class PriceFeignClientFallbackFactory implements FallbackFactory<PriceFeignClient> {
    
        @Override
        public PriceFeignClient create(Throwable throwable) {
            return new PriceFeignClient() {
                @Override
                public PriceInfo getPrice(Integer id) {
                    log.error("远程调用被限流或降级了,throwable={}",throwable);
                    PriceInfo priceInfo = new PriceInfo();
                    priceInfo.setId(id);
                    priceInfo.setPrice(new BigDecimal(id));
                    return priceInfo;
                }
            };
        }
    }
    

    相关源码:org.springframework.cloud.alibaba.sentinel.feign.SentinelFeign

    Sentinel使用方式总结

    使用方式 使用方式 使用方法
    编码方式 API try...catch...finally
    注解方式 @SentinelResource blockHandler / fallback
    RestTemplate @SentinelRestTemplate blockHandler / fallback
    Feign feign.sentinel.enabled=true fallback / fallbackFactory

    Sentinel规则持久化方案推荐:

    Alibaba Sentinel规则持久化-推模式-手把手教程【基于Nacos】

    https://github.com/eacdy/Sentinel-Dashboard-Nacos/releases

    https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel#pull模式

    参考:

    https://www.jianshu.com/p/0e218ef7f505

    https://blog.csdn.net/sheinenggaosuwo/article/details/86592893

    https://blog.csdn.net/xudawenfighting/article/details/80127279

    阿里熔断限流Sentinel研究

    阿里sentinel源码研究深入

    https://www.jianshu.com/p/ed57014e1abb

    Sentinel-开源版本Dashboard集成Apollo配置中心

    阿里Sentinel控制台源码修改-对接Apollo规则持久化

    Alibaba Sentinel 配置项总结

    SentinelResource注解 属性总结

    相关文章

      网友评论

        本文标题:Spring Cloud Alibaba Sentinel组件

        本文链接:https://www.haomeiwen.com/subject/cilnictx.html