Sentinel配合Nacos存储规则

Sentinel配合Nacos存储规则

Sentinel是Alibaba下的控流框架。在Spring Cloud Alibaba的整合封装之下,接口限流这件事情可以非常轻易的整合到我们的Spring Cloud应用中。

Sentinel通过制定资源控流规则,来进行控制客户端服务器流量。Sentinel教程:http://zssaer.cn/2021/08/20/sentinel/

而正常情况下我们使用Nacos作为微服务发现服务时,Nacos的管理配置功能也可以用来管理Sentinel的控流规则,从而实现规则配置持久化。

Sentinel自身就支持了多种不同的数据源来持久化规则配置,目前包括以下几种方式:

  • 文件配置
  • Nacos配置
  • ZooKeeper配置
  • Apollo配置

下面介绍如何使用Nacos来存储管理Sentinel的规则配置。

准备工作

需要同时用到Nacos和Sentinel控制台,所以确保两个服务都应该启动。

如果还没入门NacosSentinel Dashboard可以通过之前的文章来学习之前的内容。

配置方法

一般我们进行微服务控流,都作用于客户端(消费提供端)。

在Spring Cloud微服务应用的pom.xml中引入Spring Cloud Alibaba的Sentinel模块和Nacos存储扩展:

 <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
        <version>1.5.2</version>
    </dependency>
</dependencies>

其中sentinel-datasource-nacos为Sentinel提供的Nacos存储扩展依赖包。

随后在Spring Cloud应用中添加配置信息:

spring:
  application:
    name: sentinel-service-datasource-nacos
  cloud:
    sentinel:
      transport:
        # Sentinel控制台地址
        dashboard: localhost:8849
      datasource:
        ds:
          nacos:
            # Nacos服务地址
            server-addr: localhost:8848
            # Nacos中存储规则的dataId
            dataId: ${spring.application.name}-flow
            # Nacos中存储规则的groupId
            groupId: DEFAULT_GROUP
            # 定义存储的规则类型
            rule-type: flow

其中rule-type中的规则类型是spring cloud alibaba升级到0.2.2之后增加的配置。在之前版本中没有这个参数。

这个参数可以通过org.springframework.cloud.alibaba.sentinel.datasource.RuleType这个枚举类中看见所能配置的类型。

前往Nacos配置管理,新增一个配置文件,用作其Sentinel的规则配置文件,其中dataId和Group需要与SpringCloud配置文件中指定的一样。

Sentinel配置文件建议使用Json文件来进行配置,其中配置内容样式如下:

[
    &#123;
        "resource": "/hello",
        "limitApp": "default",
        "grade": 1,
        "count": 5,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    &#125;
]

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

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

这里面的详细配置和其Sentinel核心配置规则属性一样。

最后运行客户端,会发现顺利Sentinel控制台已经从Nacos服务上获取到了规则配置文件:

更新配置

由于Nacos配置中心管理是动态刷新的,所以在Nacos 上进行更新Sentinel规则配置可以实现动态更新。

但是通过在其Sentinel控制台中修改规则配置的话,仅仅储存在于其服务的内存中,不会修改其Nacos配置管理中的值,并且客户端重启后其规则配置值会复原。

Sentinel控制台同步Nacos

其实可以通过改造Sentinel控制台的源码来进行实现其Sentinel控制台修改规则同步到Nacos配置管理中。

下面来介绍然后改造Sentinel控制台源码,来实现个性化的控制台修改到Nacos配置管理上去:

首先需要下载一份Sentinel源码,克隆一份官方GitHub项目:https://github.com/alibaba/Sentinel

其中项目中控制台项目源码在sentinel-dashboard文件夹中,将其项目导入IDE。

  1. 修改pom文件,将其中sentinel-datasource-nacos的依赖的test属性注释去掉,从而在正式环境中使用它。
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!--<scope>test</scope>-->
</dependency>
  1. 找到resources/app/scripts/directives/sidebar/sidebar.html中的这段代码
<li ui-sref-active="active" ng-if="!entry.isGateway">
    <a ui-sref="dashboard.flowV1(&#123;app: entry.app&#125;)">
     <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
</li>

将其修改flowV1修改为flow

<li ui-sref-active="active" ng-if="!entry.isGateway">
     <a ui-sref="dashboard.flow(&#123;app: entry.app&#125;)">
     <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
</li>
  1. com.alibaba.csp.sentinel.dashboard.rule包下新建一个nacos包,用来编写针对Nacos的扩展实现。
  1. 在其创建的nacos包中创建一个NacosConfig类,当做Nacos的配置类。
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;
import java.util.Properties;

/**
 * @description: Nacos配置类
 * @author: Zhaotianyi
 * @time: 2021/8/20 15:19
 */
@Configuration
public class NacosConfig &#123;

    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() &#123;
        return JSON::toJSONString;
    &#125;

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() &#123;
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    &#125;

    @Bean
    public ConfigService nacosConfigService() throws Exception &#123;
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, "localhost:8080");
        return ConfigFactory.createConfigService(properties);
    &#125;
&#125;

其中的ConfigService类需要在Pom文件中导入nacos-api包。

PropertyKeyConst.SERVER_ADDR的值对应的Nacos的服务器地址和端口号。

如果用到了namespace隔离环境,可以在nacosConfigService方法中再加入配置,比如:properties.put(PropertyKeyConst.NAMESPACE, "130e71fa-97fe-467d-ad77-967456f2c16d");

  1. 将其test文件夹下的com.alibaba.csp.sentinel.dashboard.rule.nacos包的FlowRuleNacosProviderFlowRuleNacosPublisher复制到正式环境下的com.alibaba.csp.sentinel.dashboard.rule.nacos包下。它们分别是Nacos配置拉取组件和Nacos配置推送组件
  1. 修改里面的FlowRuleNacosProvider 类,实现Nacos配置拉取:
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> &#123;

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;

    public static final String FLOW_DATA_ID_POSTFIX="-flow";
    public static final String GROUP_ID="DEFAULT_GROUP";

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception &#123;
        //String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
        //    NacosConfigUtil.GROUP_ID, 3000);
        String rules = configService.getConfig(appName + FLOW_DATA_ID_POSTFIX, GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) &#123;
            return new ArrayList<>();
        &#125;
        return converter.convert(rules);
    &#125;
&#125;

其中的FLOW_DATA_ID_POSTFIX常量为 Nacos配置文件的Data后缀,这儿是流控规则,之前在客户端中SpringCloud配置文件dataId设置为${spring.application.name}-flow所以这儿使用-flow,而GROUP_ID代表存储GROUP。这里根据自己实际情况来进行更改。

  1. 修改里面的FlowRuleNacosPublisher类,实现Nacos配置推送:
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> &#123;

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<FlowRuleEntity>, String> converter;

    public static final String FLOW_DATA_ID_POSTFIX = "-flow";
    public static final String GROUP_ID = "DEFAULT_GROUP";

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception &#123;
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) &#123;
            return;
        &#125;
//        configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
//            NacosConfigUtil.GROUP_ID, converter.convert(rules));
        configService.publishConfig(app + FLOW_DATA_ID_POSTFIX,
                GROUP_ID, converter.convert(rules));
        &#125;
&#125;

这里的常量规则和拉取类一样。

  1. 修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2DynamicRuleProviderDynamicRulePublisher注入的Bean,将其改为上面两个类
@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
  1. 修改成功,运行即可,这时当我们通过Sentinel控制台来修改流控规则配置,其直接会推送到Nacos配置管理上。这样配置文件在Sentinel控制台和Nacos实现了双向绑定效果。
  2. 最后,测试完毕后,使用Maven打包Sentinel控制台源码,实现外部部署
mvn clean package

注意:当使用Sentinel控制台修改后,Nacos推送得到的配置文件将会变为TEXT格式,但由于Json格式和Text格式相通,所以不需要担心出错。