Sentinel

Sentinel

Sentinel 是阿里巴巴开源的一款断路器实现,本身在阿里内部已经被大规模采用,非常稳定。

Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。

Sentinel 具有以下特征:

丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒级数据, 甚至 500 台以下规模的集群的汇总运行情况。

广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块, 例如与 Spring Cloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口快

速地定制逻辑。例如定制规则管理、适配动态数据源等

Sentinel 分为两个部分:

核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo /Spring Cloud 等框架也有较好的支持。

控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

微服务集成Sentinel

1.加依赖


    com.alibaba.cloud
    spring-cloud-starter-alibaba-sentinel

2.编写对应 controller 并调用即可

安装Sentinel控制台

官网或者gitee下jar

注意cloud的版本对应

sentinel的jar要注意jdk不要过高

启动控制台

# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)

java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar

 或者

java -Dserver.port=8888 -jar sentinel-dashboard-1.8.1.jar

修改 applicatio配置文件 ,在里面加入有关控制台的配置

spring.cloud.sentinel.transport.port=9999
# 该端口号为sentinal于服务之间的交互  随便写只要不被占用
spring.cloud.sentinel.transport.dashboard=localhost:8888
#sentinal服务所在的地址和端口号

通过浏览器访问

输入localhost:8888 进入控制台 ( 默认用户名密码是 sentinel/sentinel )

微服务访问后,sentinel的控制台会进行监听显示

Sentinel的控制台其实就是一个SpringBoot编写的程序。我们需要将我们的微服务程序注册到控制台上, 即在微服务中指定控制台的地址, 并且还要开启一个跟控制台传递数据的端口, 控制台也可以通过此端口调用微服务中的监控程序获取微服务的各种信息。

规则

Sentinel_第1张图片

资源名: 微服务的请求路径名字 注意必须带 /

链路流控模式下:

        注解:@SentinelResource 可以设置资源名字

        如:

                @SentinelResource("message") 

                此时资源名为 message 不用带 /

资源名:唯一名称,默认是请求路径,可自定义

针对来源:指定对哪个微服务进行限流,默认指default,意思是不区分来源,全部限制

阈值类型/单机阈值

l QPS(每秒请求数量): 当调用该接口的QPS达到阈值的时候,进行限流

l 线程数:当调用该接口的线程数达到阈值的时候,进行限流

并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。

是否集群:暂不需要集群

直接

故名思与,超过所配置的每秒的访问次数就会进行限流

关联

选择后会显示一个关联资源

如 我们设置了一个资源名/test  关联资源为 /test01

此时我们设置了 QPS 1

我们对 /test01 进行访问超过规则 /test 会被限流

链路

选择后会显示一个入口资源

资源名填写为我们通过@SentinelResource设置的资源名 不加/

入口资源为我们要指定的进行限流的请求路径 加/

服务熔断

熔断默认是一个报错异常页面

可以通过 注解自定义异常页面内容

自定义业务类中的方法必须为静态方法


    @SentinelResource(value = "资源名",
            blockHandlerClass = 自定义业务类.class,
            blockHandler = "自定义业务类中的静态方法")
   

配置流控效果

l 快速失败(默认): 直接失败,抛出异常,不做任何额外的处理,是最简单的效果

l Warm Up:它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的1/3,然后慢慢.增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。

设置阈值为 10,预热时间是 5 秒,逐渐增加到阈值上限,所以会有一个初始阈值

初始阈值 = 阈值上限 / coldFactor, coldFactor 是冷加载因子,默认为3

上面这个配置的效果就是:在 5 秒内最大阈值是 3(10/codeFactor),超过5秒,最大阈值为10

Sentinel_第2张图片

l 排队等待:让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。

排队等待方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。

注意:匀速排队,让请求以匀速通过,阈值类型必须设置为QPS,否则无效,匀速排队模式暂时不支持 QPS > 1000 的场景

Sentinel_第3张图片

当阈值设为 2 的时候,则代表一秒匀速的通过 2 个请求,也就是每个请求平均间隔恒定为 1000 / 2 = 500 ms,每一个请求的最长等待时间(maxQueueingTimeMs)为 1s 。

降级规则

降级规则就是设置当满足什么条件的时候,对服务进行降级。Sentinel提供了三个衡量条件:

l 平均响应时间 :当资源的平均响应时间超过阈值(以 ms 为单位)之后,资源进入准降级状态。如果接下来 1s 内持续进入 5 个请求,它们的 RT都持续超过这个阈值,那么在接下的时间窗口(以 s 为单位)之内,就会对这个方法进行服务降级。

Sentinel_第4张图片

RT:平均响应时间 (DEGRADE_GRADE_RT);当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下来的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。

注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms

若需要 变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置

l 异常比例:当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的时间窗口智能,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0,1.0],代表0%~100%

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

热点规则

热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。

热点规则增强使用

参数例外项允许对一个参数的具体值进行流控编辑刚才定义的规则,增加参数例外项

授权规则:

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源 访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过: 若配置白名单,则只有请求来源位于白名单内时才可通过; 若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过

Sentinel_第5张图片

流控应用怎么填写呢?

其实这个位置要填写的是来源标识,Sentinel提供了 RequestOriginParser 接口来处理来源。 只要Sentinel保护的接口资源被访问,Sentinel就会调用 RequestOriginParser 的实现类去解析 访问来源。

Sentinel规则持久化

我们已经知道,可以通过Dashboard来为每个Sentinel客户端设置各种各样的规则,但是这里有一个问题,就是这些规则默认是存放在内存中,极不稳定,所以需要将其持久化。

方法1:添加到nacos里面

1)在配置文件application中添加以下内容

spring.cloud.sentinel.datasource.ds1.nacos.data-id=${spring.application.name}

spring.cloud.sentinel.datasource.ds1.nacos.data-type=json
spring.cloud.sentinel.datasource.ds1.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds1.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds1.nacos.rule-type=flow

2)在nacos中为cloudalibaba-sentinel-service(可以在配置文件自定义)服务添加对应配置。

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

resource: 资源名称;

imitApp: 来源应用;

grade: 阈值类型,0表示线程数,1表示QPS;

count: 单机阈值;

strategy: 流控模式,0表示直接,1表示关联,2表示链路;

controlBehavior: 流控效果,0表示快速失败,1表示。Warm Up,2表示排队等待。

clusterMode: 是否集群。

2. 使用文件

package com.pro.config;


import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class FilePersistence implements InitFunc {
    @Value("${spring.application.name}")
    private String appcationName;
    @Override
    public void init() throws Exception {
        String ruleDir = System.getProperty("user.home") + "/sentinel-rules/" + appcationName;
        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.register2Property(flowRuleRDS.getProperty());
        WritableDataSource> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        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<>(
                authorityRulePath,
                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);
    }
}

将这个配置文件使用

在对应的微服务下面的resources里面创建目录

META-INF/services

并在目录下面写一个文件

com.alibaba.csp.sentinel.init.InitFunc

文件里面写的是刚才的配置类的包名加类名 :com.pro.config.FilePersistence

你可能感兴趣的:(JAVA,sentinel)