玩转Spring Cloud Alibaba之Sentinel

目录

前言

常见的容错方案:

1.1使用步骤

1.引入库

2.在yml增加暴露auctor端点选项

二、整合sentinel控制台到项目(sentinel使用懒加载模式)

二、使用Sentinel实现限流

2.1在sentinel控制台进行配置,实现限流

流控模式

流控效果

三、使用Sentinel实现降级(断路器模式)

三、使用Sentinel实现热点规则

四、使用Sentinel实现系统保护规则

五、使用Sentinel授权规则

六、Sentinel APi的使用

七、Sentinel整合RestTemplate

 八、Sentinel整合Feign

九、Sentinel规则的持久化

9.1拉模式

9.2拉模式


前言

现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用,这便是著名的雪崩效应。

⬆️阿里官方文档描述,说人话版本⬇️

微服务架构系统往往包含很多微服务,各个微服务之间通过轻量级的通信机制进行通信,构成一个完整的应用系统。但每个微服务都不是100%可用的,各个微服务之间的调用构成一个复杂的链路网,如果其中一个微服务挂掉,那么调用方将一直等待提供方响应,线程进入等待/阻塞状态。随着时间推移,陷入等待的线程可能越来越多,当达到了最大线程数,服务将不能再创建新的线程,也就意味着无法再提供服务,进而引起整个微服务群故障。

Sentinel是阿里巴巴出品的面向微服务架构的流量控制组件,主要以资源为切入点,从限流,降级,熔断等多个维度对服务进行保护。

常见的容错方案:

        1、「请求超时」

        思想:只要跑得快,我就不容易被猪队友拖死

        为每次请求设置一个比较短的超时时间,无论请求是否成功,达到设定时间后,线程就会被释放。只要线程释放的速度足够快,调用方就不至于那么容易被服务提供方拖死。

        2、「限流」

        思想:我效率就这么高,人再多我也没办法

        给一个资源实例设定一个qps值,超过阈值,直接拒绝服务,通过这种方式保护自己。

        3、「仓壁模式」

玩转Spring Cloud Alibaba之Sentinel_第1张图片

        思想:你挂归你挂,不关我的事

        每个资源之间相互隔离,例如以controller举例,每个Controller对应一个线程池,当一个线程池挂掉,不影响另一个Controller的正常工作。

        4、「断路器」

        思想:通过监控+开关,比较智能

        断路器的三种状态的转换

玩转Spring Cloud Alibaba之Sentinel_第2张图片

1、当服务A去调用服务B,调用成功,则断路器处于关闭状态

2、当调用服务B失败,错误率或失败次数达到某个阈值,断路器将打开,中断整个链路。当过一段时间后,断路器将处于半开状态,这时允许一小部分流量(1个)来继续调用服务B,如果服务调用成功,断路器将关闭,如果继续失败,则继续回到打开状态,过一会再进入半开。


一、给项目引入sentinel

1.1使用步骤

1.引入库


    org.springframework.cloud
    spring-cloud-starter-alibaba-sentinel

2.在yml增加暴露auctor端点选项

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

访问localhost:port/auctor/sentinel出现以下页面,说明成功

玩转Spring Cloud Alibaba之Sentinel_第3张图片

二、整合sentinel控制台到项目(sentinel使用懒加载模式)

1、去sentinel官网下载项目对应的sentinel版本,并启动Release v1.8.6 · alibaba/Sentinel · GitHub

2、在项目yml指定sentinel控制台的地址

sentinel:      
    transport:
        # 指定sentinel 控制台的地址
        dashboard: localhost:8080

二、使用Sentinel实现限流

2.1在sentinel控制台进行配置,实现限流

玩转Spring Cloud Alibaba之Sentinel_第4张图片

针对来源:可以根据不同的(微服务)调用来源设置不同的qps值,这里默认为default,默认对所有调用方生效

流控模式

        直接:当资源(接口)达到多少阈值,就限流

        关联:保护关联资源的一种设计,当关联的资源(可以是一个接口)达到设定的阈值,就限流自己

玩转Spring Cloud Alibaba之Sentinel_第5张图片

        链路:只记录指定链路上的流量。

玩转Spring Cloud Alibaba之Sentinel_第6张图片

                                   链路设置示意图 

流控效果

        快速失败:        直接失败,抛异常

        warm up:        预热模式(会丢弃请求),让阈值缓慢的增加,经过预热时长,逐渐让qps渐升到设定的值。初始值=设定的阈值/冷加载因子(默认是3)。【适用于秒杀场景】

        排队等待:        不丢弃请求,让请求以均匀的速通过,如果用改种方式,阈值类型必须选择qps,不能使用线程,还有超时时间选项,意思是达到这个超时时间,就丢弃掉该请求,处理下一个请求。【适用突然繁忙,但大多数时间空闲的场景】


三、使用Sentinel实现降级(断路器模式)

玩转Spring Cloud Alibaba之Sentinel_第7张图片

 降级策略:

        RT:平均响应时间(单位s)「最大值为4900ms」。

        如果RT超过设定阈值,并且在时间窗口内进入的请求>=5,就会触发降级。时间窗口结束,才会关闭降级

        异常比例:统计的是秒级别的

        异常数:分钟级别,时间窗口<60可能会出问题


三、使用Sentinel实现热点规则

热点规则:比较特殊的流控规则,支持根据特定参数来设定限流规则

使用场景:存在热点参数,某些参数的qps特别高,并且希望提升api可用性的场景

Quick Start

1、新建测试端点

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

2、热点规则设置

玩转Spring Cloud Alibaba之Sentinel_第8张图片

当第一个参数a=1,就会触发这个热点规则。a!=1或a为null,都不会触发流控

展开高级选项,还可以设置参数例外项,并且支持多种基础数据类型(注意:参数类型必须为string)


四、使用Sentinel实现系统保护规则

该规则实际生产环境用的比较少

玩转Spring Cloud Alibaba之Sentinel_第9张图片

LOAD:当系统load1(1分钟的load) 超过阈值,且并发线程数超过系统容量时触发,建议设置为cpu核心数*2.5(仅对Unix生效)

linux查看load命令:uptime

RT:所有入口流量的平均RT达到阈值就触发

线程数:所有入口流量的并发线程数达到阈值触发

入口QPS:所有入口流量的QPS达到阈值触发

五、使用Sentinel授权规则

玩转Spring Cloud Alibaba之Sentinel_第10张图片

/shares/1该资源,只允许test的这个微服务调用 


六、Sentinel APi的使用

 @GetMapping("/test-sentinel-resource")
    @SentinelResource(
        value = "test-sentinel-api",    //注意:这里只是定义了资源名,具体流控/降级规则还需在sentinel控制台进行配置

        blockHandler = "block",
        blockHandlerClass = TestControllerBlockHandlerClass.class,
        fallback = "fallback"
    )
    public String testSentinelResource(@RequestParam(required = false) String a) {
        if (StringUtils.isBlank(a)) {
            throw new IllegalArgumentException("a cannot be blank.");
        }
        return a;
    }



    /**
     * 1.5 处理降级
     * - sentinel 1.6 可以处理Throwable(抛出的异常)
     *
     * @param a
     * @return
     */
    public String fallback(String a) {
        return "限流,或者降级了 fallback";
    }
TestControllerBlockHandlerClass.class
@Slf4j
public class TestControllerBlockHandlerClass {
    /**
     * 处理限流或者降级
     *
     * @param a
     * @param e
     * @return
     */
    public static String block(String a, BlockException e) {
        log.warn("限流,或者降级了 block", e);
        return "限流,或者降级了 block";
    }
}

七、Sentinel整合RestTemplate

在RestTemplateConfig添加 @SentinelRestTemplate注解

    @GetMapping("/test-rest-template-sentinel/{userId}")
    public UserDTO test(@PathVariable Integer userId) {
        return this.restTemplate
            .getForObject(
                "http://user-center/users/{userId}",
                UserDTO.class, userId);
    }

调用一次该接口后便能在sentinel控制台看到对应资源,对其进行限流。

再调用便能看到限流成功

玩转Spring Cloud Alibaba之Sentinel_第11张图片

 八、Sentinel整合Feign


@FeignClient(name = "user-center",
    fallbackFactory = UserCenterFeignClientFallbackFactory.class
)
public interface UserCenterFeignClient {
    /**
     * http://user-center/users/{id}
     *
     * @param id
     * @return
     */
    @GetMapping("/users/{id}")
    UserDTO findById(@PathVariable Integer id);
}
UserCenterFeignClientFallbackFactory.class
@Component
@Slf4j
public class UserCenterFeignClientFallbackFactory implements FallbackFactory {
    @Override
    public UserCenterFeignClient create(Throwable cause) {
        return new UserCenterFeignClient() {
            @Override
            public UserDTO findById(Integer id) {
                log.warn("远程调用被限流/降级了", cause);
                UserDTO userDTO = new UserDTO();
                userDTO.setWxNickname("流控/降级返回的用户");
                return userDTO;
            }
        };
    }
}

九、Sentinel规则的持久化

9.1拉模式

玩转Spring Cloud Alibaba之Sentinel_第12张图片

思想:将规则持久化到本地,每次服务启动先读取JSON文件

一、FileRefreshableDataSource定时从指定文件中读取json文件,如果发现文件发生变化,更新规则缓存;

二、FileWritableDataSource接受控制台规则推送,并根据配置,修改json规则文件

步骤:

1、加依赖


      com.alibaba.csp
      sentinel-datasource-extension

2、添加类FileDataSourceInit


public class FileDataSourceInit implements InitFunc {
    @Override
    public void init() throws Exception {
        // TIPS: 如果你对这个路径不喜欢,可修改为你喜欢的路径
        String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";

        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);

        // 流控规则
        ReadableDataSource> flowRuleRDS = new FileRefreshableDataSource<>(
            flowRulePath,
            flowRuleListParser
        );
        // 将可读数据源注册至FlowRuleManager
        // 这样当规则文件发生变化时,就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource> flowRuleWDS = new FileWritableDataSource<>(
            flowRulePath,
            this::encodeJson
        );
        // 将可写数据源注册至transport模块的WritableDataSourceRegistry中
        // 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        // 降级规则
        ReadableDataSource> degradeRuleRDS = new FileRefreshableDataSource<>(
            degradeRulePath,
            degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource> degradeRuleWDS = new FileWritableDataSource<>(
            degradeRulePath,
            this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        // 系统规则
        ReadableDataSource> systemRuleRDS = new FileRefreshableDataSource<>(
            systemRulePath,
            systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource> systemRuleWDS = new FileWritableDataSource<>(
            systemRulePath,
            this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        // 授权规则
        ReadableDataSource> authorityRuleRDS = new FileRefreshableDataSource<>(
            flowRulePath,
            authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource> authorityRuleWDS = new FileWritableDataSource<>(
            authorityRulePath,
            this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        // 热点参数规则
        ReadableDataSource> paramFlowRuleRDS = new FileRefreshableDataSource<>(
            paramFlowRulePath,
            paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource> paramFlowRuleWDS = new FileWritableDataSource<>(
            paramFlowRulePath,
            this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    private Converter> flowRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference>() {
        }
    );
    private Converter> degradeRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference>() {
        }
    );
    private Converter> systemRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference>() {
        }
    );

    private Converter> authorityRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference>() {
        }
    );

    private Converter> paramFlowRuleListParser = source -> JSON.parseObject(
        source,
        new TypeReference>() {
        }
    );

    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private  String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}

3、在项目resource/META-INF/servies目录出刚健文件,名为com.alibaba.csp.sentinel.init.InitFunc,内容

#改成上面init类的全包名
com.jaychan.contentcenter.sentineltest.FileDataSourceInit

4、该方式的优缺点

优点:简单易懂,没有多余依赖

缺点:

1、因为规则是定时更新的,所以规则有延迟。如果定时时间设置过大,长时间会有延迟,如果时间设置过小,又影响性能

2、规则存储在本地文件,如果有一天需要迁移微服务,需要吧规则文件一起迁移,否则会丢失

9.2拉模式

玩转Spring Cloud Alibaba之Sentinel_第13张图片

通过Nacos进行规则的同步,同时也会监听nacos里面的规则,实时更新

改动比较大,具体操作参考官方文档

在生产环境中使用 Sentinel · alibaba/Sentinel Wiki · GitHub

懒人包:

GitHub - eacdy/Sentinel-Dashboard-Nacos: Description Sentinel Dashboard使用NACOS作为数据源持久化规则。【仅用于教学,如用于生产,请务必做好测试!】

你可能感兴趣的:(Spring,Cloud,个人学习,Spring,java,spring,cloud)