首页 > 其他分享 >Spring Cloud Alibaba——Sentinel规则持久化

Spring Cloud Alibaba——Sentinel规则持久化

时间:2023-01-17 18:03:15浏览次数:88  
标签:Spring Alibaba sentinel datasource 规则 Sentinel alibaba com cloud

一、在生产环境中使用Sentinel

生产环境的Sentinel Dashboard需要具备下面几个特性:

  • 1、规则管理及推送,集中管理和推送规则。
  • 2、监控,支持可靠、快速的实时监控和历史监控数据查询。
  • 3、权限控制,区分用户角色,来进行操作。

1、规则管理及推送

一般来说,规则的推送有下面三种模式:

推送模式 说明 优点 缺点
原始模式 API 将规则推送至客户端并直接更新到内存中,扩展写数据源 简单,无任何依赖 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境
Pull 模式 扩展写数据源, 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件等 简单,无任何依赖;规则持久化 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。
Push 模式 扩展读数据源ReadableDataSource,规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 规则持久化;一致性;快速 引入第三方依赖

2、动态规则扩展

Sentinel的理念是开发者只需要关注资源的定义,当资源定义成功后可以动态增加各种流控降级规则。

 

Sentinel提供两种方式修改规则:

  • 1、通过API直接修改(loadRules)——手动修改。
  • 2、通过 DataSource 适配不同数据源修改——动态规则。

手动通过API修改比较直观,可以通过以下几个API修改不同的规则:

FlowRuleManager.loadRules(List<FlowRule> rules);  // 修改流控规则

DegradeRuleManager.loadRules(List<DegradeRule> rules);  // 修改降级规则

手动修改规则(硬编码方式)一般仅用于测试和演示,生产上一般通过动态规则源的方式来动态管理规则。示例代码如下所示:

//TODO 发布限流规则方式一:手动通过API修改
final String rule = "[\n"
        + "  {\n"
        + "    \"resource\": \"login\",\n"
        + "    \"controlBehavior\": 0,\n"
        + "    \"count\": 5.0,\n"
        + "    \"grade\": 1,\n"
        + "    \"limitApp\": \"default\",\n"
        + "    \"strategy\": 0\n"
        + "  }\n"
        + "]";

ConfigService configService = NacosFactory.createConfigService(serverAddr);
boolean isPublishOk = configService.publishConfig(dataId, group, rule);
System.out.println(isPublishOk);

上述 loadRules() 方法只接受内存态的规则对象,但更多时候规则存储在文件、数据库或者配置中心当中。DataSource 接口给我们提供了对接任意配置源的能力。相比直接通过API 修改规则,实现 DataSource 接口是更加可靠的做法。

 

我们推荐通过控制台设置规则后将规则推送到统一的规则中心,客户端实现 ReadableDataSource 接口端监听规则中心实时获取变更,流程如下:

原始模式

如果不做任何修改,Dashboard的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中:

这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。

二、DataSource扩展方式介绍

1、DataSource扩展常见的实现方式有:

  • 拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是RDBMS、文件,甚至是VCS等。这样做的方式是简单,缺点是无法及时获取变更。

  • 推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。

Sentinel目前支持以下数据源扩展:

Pull 模式

原理:

  • 扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等。
  • pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。

过程如下:

Pull Demo

  • 1、添加配置
<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
  • 2、编写持久化代码,实现com.alibaba.csp.sentinel.init.InitFunc
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 java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * FileDataSourceInit for : 自定义Sentinel存储文件数据源加载类
 */
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 hotParamFlowRulePath = ruleDir + "/param-flow-rule.json";

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

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

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

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

        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> hotParamFlowRuleRDS = new FileRefreshableDataSource<>(
                hotParamFlowRulePath,
                hotParamFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(hotParamFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                hotParamFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    /**
     * 流控规则对象转换
     */
    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );
    /**
     * 降级规则对象转换
     */
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );
    /**
     * 系统规则对象转换
     */
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {
            }
    );

    /**
     * 授权规则对象转换
     */
    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );

    /**
     * 热点规则对象转换
     */
    private Converter<String, List<ParamFlowRule>> hotParamFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );

    /**
     * 创建目录
     *
     * @param filePath
     */
    private void mkdirIfNotExits(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * 创建文件
     *
     * @param filePath
     * @throws IOException
     */
    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}
  • 3、启用上述代码 resource 目录下创建 resources/META-INF/services 目录并创建文件com.alibaba.csp.sentinel.init.InitFunc ,内容为:
com.sxzhongf.sharedcenter.configuration.sentinel.datasource.FileDataSourceInit

Pull 优缺点

  • 优点

    • 1、简单,无任何依赖
    • 2、没有额外依赖
  • 缺点

  • 1、不保证一致性(规则是使用FileRefreshableDataSource定时更新,会有延迟)

  • 2、实时性不保证(规则是使用FileRefreshableDataSource定时更新)

  • 3、拉取过于频繁也可能会有性能问题

  • 4、由于文件存储于本地,容易丢失

  • 参考资料:

Push 模式

生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper、Nacos、Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:

 

推荐通过控制台设置规则后将规则推送到统一的规则中心,客户端实现 ReadableDataSource接口端监听规则中心实时获取变更,流程如下:

实现原理
  • 控制台推送规则到Nacos/远程配置中心
  • Sentinel client 监听Nacos配置变化,更新本地缓存

Sentinel dashboard 加工

Dashboard 规则改造主要通过2个接口:

com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider
com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher
<!-- for Nacos rule publisher sample -->
<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-datasource-nacos</artifactId>
	<!--注释掉原文件中的scope,让其不仅在test的时候生效-->
	<!--<scope>test</scope>-->
</dependency>
  • 偷懒模式:复制sentinel-dashboard项目下test下的nacos包( src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacossrc/main/java/com/alibaba/csp/sentinel/dashboard/rule

修改NacosConfig中的指向Nacos 的ip和端口

@Configuration
public class NacosConfig {

    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public ConfigService nacosConfigService() throws Exception {
		//设置Nacos的ip和端口
        return ConfigFactory.createConfigService("localhost:8848");
    }
}
  • 修改controller中的默认provider & publisher com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2中
@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {

    @Autowired
    //@Qualifier("flowRuleDefaultProvider")
    @Qualifier("flowRuleNacosProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
	
    @Autowired
    //@Qualifier("flowRuleDefaultPublisher")
    @Qualifier("flowRuleNacosPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
	
}
  • 打开 ·/Sentinel-1.6.2/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html·文件,修改代码:
<!--<li ui-sref-active="active" ng-if="entry.appType==0">-->
	<!--<a ui-sref="dashboard.flow({app: entry.app})">-->
		<!--<i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 V1</a>-->
<!--</li>-->

------------改为

<li ui-sref-active="active" ng-if="entry.appType==0">
	<a ui-sref="dashboard.flow({app: entry.app})">
		<i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 Nacos</a>
</li>

Dashboard中要修改的代码已经好了。

 

重新启动 Sentinel-dashboard mvn clean package -DskipTests

相关微服务设置

  • 添加依赖
  <dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>sentinel-datasource-nacos</artifactId>
  </dependency>
  • 添加配置
spring:
  cloud:
    sentinel:
      datasource:
        yibo_flow:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            # 规则类型,取值见:org.springframework.cloud.alibaba.sentinel.datasource.RuleType
            rule_type: flow
        yibo_degrade:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            rule-type: degrade

测试效果

Sentinel 添加流控规则:

Nacos 查看同步的配置:

动态数据源支持

SentinelProperties 内部提供了 TreeMap 类型的 datasource 属性用于配置数据源信息。

spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json
spring.cloud.sentinel.datasource.ds1.file.rule-type=flow
#spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json
#spring.cloud.sentinel.datasource.ds1.file.data-type=custom
#spring.cloud.sentinel.datasource.ds1.file.converter-class=com.alibaba.cloud.examples.JsonFlowRuleListConverter
#spring.cloud.sentinel.datasource.ds1.file.rule-type=flow

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

spring.cloud.sentinel.datasource.ds3.zk.path = /Sentinel-Demo/SYSTEM-CODE-DEMO-FLOW
spring.cloud.sentinel.datasource.ds3.zk.server-addr = localhost:2181
spring.cloud.sentinel.datasource.ds3.zk.rule-type=authority

spring.cloud.sentinel.datasource.ds4.apollo.namespace-name = application
spring.cloud.sentinel.datasource.ds4.apollo.flow-rules-key = sentinel
spring.cloud.sentinel.datasource.ds4.apollo.default-flow-rule-value = test
spring.cloud.sentinel.datasource.ds4.apollo.rule-type=param-flow

配置说明:

spring.cloud.sentinel.transport.dashboard:sentinel dashboard的访问地址,根据上面准备工作中启动的实例配置。
spring.cloud.sentinel.datasource.ds.nacos.server-addr:nacos的访问地址,根据上面准备工作中启动的实例配置。
spring.cloud.sentinel.datasource.ds.nacos.groupId:nacos中存储规则的groupId。
spring.cloud.sentinel.datasource.ds.nacos.dataId:nacos中存储规则的dataId。
spring.cloud.sentinel.datasource.ds.nacos.rule-type:该参数是spring cloud alibaba升级到0.2.2之后增加的配置,用来定义存储的规则类型。

所有的规则类型可查看枚举类:org.springframework.cloud.alibaba.sentinel.datasource.RuleType,每种规则的定义格式可以通过各枚举值中定义的规则对象来查看,比如限流规则可查看:com.alibaba.csp.sentinel.slots.block.flow.FlowRule。这里对于dataId使用了${spring.application.name}变量,这样可以根据应用名来区分不同的规则配置。

注意:

由于版本迭代关系,Github Wiki中的文档信息不一定适用所有版本。比如:并没有spring.cloud.sentinel.datasource.ds2.nacos.rule-type这个参数。所以,读者在使用的时候,可以通过查看org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration和org.springframework.cloud.alibaba.sentinel.datasource.config.NacosDataSourceProperties两个类来分析具体的配置内容,会更为准确。

  • Nacos中创建限流规则的配置,比如:springcloudalibaba-sentinel-nacos.json
[
    {
        "resource":"/sentinelNacos/hello",
        "limitApp":"default",
        "grade":1,
        "count":3,
        "strategy":0,
        "controlBehavior":0,
        "clusterMode":false
    }
]

可以看到上面配置规则是一个数组类型,数组中的每个对象是针对每一个保护资源的配置对象,每个对象中的属性解释如下:

resource:资源名,即限流规则的作用对象
  • limitApp:流控针对的调用来源,若为 default 则不区分调用来源。
  • grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制。
  • count:限流阈值。
  • strategy:调用关系限流策略。
  • controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)。
  • clusterMode:是否为集群模式。

参考: https://www.bookstack.cn/read/sentinel-v1.7/9eec10a866f4e671.md

https://blog.csdn.net/m0_37661458/article/details/106605477

https://www.pianshen.com/article/44641395129/

https://www.cnblogs.com/zhangpan1244/p/11228020.html

标签:Spring,Alibaba,sentinel,datasource,规则,Sentinel,alibaba,com,cloud
From: https://blog.51cto.com/u_14014612/6017544

相关文章