前言:
最近在体验Spring Cloud Alibaba,其中用到了Sentinel-Dashboard组件提供限流熔断
在Sentinel-Dashboard中配置规则是存储在内存之中,重启应用就会丢失,所以实际生产环境中需要配置规则的持久化实现。
Sentinel提供多种不同的数据源来持久化规则配置,包括file,redis、nacos、zk、apollo。
因为正在尝试使用Spring Cloud Alibaba组件,并且也上了Nacos作为项目的注册中心与配置中心,所以考虑用Nacos来作为Sentinel规则的远程配置中心。

一、准备工作

Sentinel-Dashboard持久化
这就需要涉及到Sentinel Dashboard的规则管理及推送功能:集中管理和推送规则。sentinel-core提供 API 和扩展接口来接收信息。开发者需要根据自己的环境,选取一个可靠的推送规则方式;同时,规则最好在控制台中集中管理。
而规则管理推送主要有以下三种模式:

官方提供三种方式

这里采用是第三种Push模式,即Sentinel-Dashboard统一管理配置,然后将规则统一推送到Nacos并持久化(生成配置文件),最后客户端监听Nacos(这一部了解使用过Nacos的话应该很熟,采用ConfigService.getConfg()方法获取配置文件),下发配置生成Rule。如下图(虚线部分不推荐):

流程图

换句话说就是实现Sentinel Dashboard与Nacos之间的相互通信:
Sentinel-Dashboard界面配置流控规则---发布/推送--->Nacos生成配置文件并持久化;
通过Nacos配置文件修改流控规则---拉取--->Sentinel-Dashboard界面显示最新的流控规则。

正因为Sentinel-Dashboard当前版本(截至目前为止是1.8.1-SNAPSHOT)暂不支持,但是可以通过改造部分源码实现此功能,具体请看下面介绍。

二、Sentinel-Dashboard流控规则源码改造须知

下载Sentinel-Dashboard-Nacos源代码,然后对sentinel-dashboard模块进行改造
下载地址:https://github.com/eacdy/Sentinel-Dashboard-Nacos.git
改造前,我们所要了解实现Sentinel-Dashboard与Nacos相互通信需要经历哪些流程或者说是缺少哪些流程,我们才好对症下药,根据我的理解我归纳总结出一下几点:

  1. 流控规则Controller入口
    Sentinel-Dashboard-Nacos的流控规则下的所有操作,都会调用sentinel-dashboard源码中的FlowControllerV1类,这个类中包含流控规则本地化的CRUD操作;
FlowControllerV1类

在com.alibaba.csp.sentinel.dashboard.controller.v2包下存在一个FlowControllerV2;类,这个类同样提供流控规则的CURD,与V1不同的是,它可以实现指定数据源的规则拉取和发布。

FlowControllerV2

官方说明:
从 Sentinel 1.4.0 开始,我们抽取出了接口用于向远程配置中心推送规则以及拉取规则:DynamicRuleProvider<T>: 拉取规则
DynamicRulePublisher<T>: 推送规则
以 Nacos 为例,若希望使用 Nacos 作为动态规则配置中心,用户可以提取出相关的类,然后只需在FlowControllerV2中指定对应的 bean 即可开启 Nacos 适配

@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

所以根据官网说明,我们知道,FlowControllerV2依赖两个非常重要的类
DynamicRuleProvider:动态规则的拉取,从指定数据源中获取控制后在Sentinel中展示。
DynamicRulePublisher:动态规则发布,将在Sentinel中修改的规则同步到指定数据源中。
只需要扩展这两个类,然后集成Nacos来实现Sentinel Dashboard规则同步。

  1. sentinel-dashboard缺少Nacos配置
    如下截图test目录,官方提供关于Nacos等持久化示例,copy至rule目录创建nacos文件目录
官方提供Nacos示例

在源码中虽然官方提供了test示例(即test目录)下关于Nacos等持久化示例但是具体的实现还需要一些细节,比如在Sentinel Dashboard配置Nacos的serverAddr、namespace、groupId,并且通过Nacos获取配置文件获取服务列表等
例如:NacosConfig中ConfigFactory.createConfigService("localhost")并没有实现创建具体的nacos config service,而是默认localhost 修改参考如下代码:

配置文件
@Configuration
public class NacosConfig {

    @Value("${nacos.address}")
    private String address;

    @Value("${nacos.namespace}")
    private String namespace;

    @Value("${nacos.username}")
    private String username;

    @Value("${nacos.password}")
    private String password;

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

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

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

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

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

    @Bean
    public ConfigService nacosConfigService() throws Exception {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, address);
        properties.put(PropertyKeyConst.NAMESPACE, namespace);
        properties.put(PropertyKeyConst.USERNAME, username);
        properties.put(PropertyKeyConst.PASSWORD, password);
        return ConfigFactory.createConfigService(properties);
    }
}

application.properties文件中也没有Nacos的相关配置,修改参考如下:

nacos.address=127.0.0.1:8848
nacos.namespace=
nacos.username=nacos
nacos.password=nacos

注意:官方只提供了一种基于流控规则模块的修改,但是sentinel-dashboard对应菜单提供了好几种规则、则都需要进行配置持久化到Nacos。但官方提供的流控规则不符合我们要求,所以修改其中代码规则如下:

image
  1. 流控规则见如下截图:


    流控规则

    参考代码:

@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        return NacosConfigUtil.getRuleEntitiesFromNacos(
                this.configService,
                appName,
                NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                FlowRuleEntity.class
        );
    }
}
流控规则
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        NacosConfigUtil.setRuleStringToNacos(
                this.configService,
                app,
                NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                rules
        );
    }
}
  1. 基于其他规则修改见上面截图、创建对应目录、几种规则代码中基本一样,其中修改对应的实体类和NacosConfigUtil.XXX对应的Nacos创建的应用名、这个默认对应一种规则,重复会导致读取配置失败不生效问题

  2. 配置Controller层方法同步配置于Nacos进行持久化;(基于流控规则 -FlowControllerV2)
    1)添加截图中配置、进行持久化操作


    添加截图中配置

2)在对应Controller 添加如下代码中的构造方法,在每个方法(CRUD)调用完毕之后,调用此方法,参数存储于Nacos中进行持久化。

private void publishRules(String app)throws Exception {
       List rules =repository.findAllByApp(app);
       rulePublisher.publish(app, rules);
}

三、流控规则持久化测试

  1. 编写technical-service-platform客户端
    (1)创建springboot应用,引入maven包pom文件如下:
 <!--  sentinel核心库 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.1-SNAPSHOT</version>
        </dependency>
        <!-- 通过nacos持久化流控规则 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
            <version>1.8.1-SNAPSHOT</version>
        </dependency>
        <!--  sentinel AspectJ 的扩展用于自动定义资源 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
            <version>1.8.1-SNAPSHOT</version>
        </dependency>
        <!-- sentinel 整合spring cloud alibaba -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
        <!-- sentinel客户端与dashboard通信依赖 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.8.1-SNAPSHOT</version>
        </dependency>

(2)配置nacos,配置sentinel-dashboard datasource信息:

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/XXX?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: XXX
    password: XXX
    driver-class-name: com.mysql.jdbc.Driver
    druid:
      db-type: com.alibaba.druid.pool.DruidDataSource
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
      datasource:
        flow:
          nacos:
            server-addr: 127.0.0.1:8848
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow
        degrade:
          nacos:
            server-addr: 127.0.0.1:8848
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            rule-type: degrade
        system:
          nacos:
            server-addr: 127.0.0.1:8848
            dataId: ${spring.application.name}-system-rules
            groupId: SENTINEL_GROUP
            rule-type: system
        authority:
          nacos:
            server-addr: 127.0.0.1:8848
            dataId: ${spring.application.name}-authority-rules
            groupId: SENTINEL_GROUP
            rule-type: authority
        param-flow:
          nacos:
            server-addr: 127.0.0.1:8848
            dataId: ${spring.application.name}-param-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: param-flow

(3)编写TestController,指定/test资源节点

package com.test.controller;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/base/api")
@CrossOrigin
public class TestController {

    @RequestMapping("/test")
    public String sayHello(){
        return "hello Wold";
    }
}
  1. 启动Nacos
    我使用的是Centos启动nacos,之后登录Nacos(具体Nacos参考阿里官方文档)
    查看起初是没有${spring.application.name}-flow-rules配置文件,也没有SENTINEL-GROUP分组;


    Nacos
  2. 启动sentinel-dashboard控制台
    (1)启动sentinel dashboard
    找到target/sentinel-dashboard.jar,执行命令:
nohup java -jar sentinel-dashboard.jar &

(2)登录sentinel-dashboard配置流控规则
1)输入localhost:8080访问sentinel-dashboard控制台(登录用户/密码默认sentinel)
2)选择technical-service-platform菜单-流控规则-新增流控规则-输入配置-新增


流控规则

如图所示,我们选择QPS阈值类型,并且count为2,流控模式为默认的直接模式,流控效果快速失败(这些配置具体含义参看官网wiki)

注意:一开始可能会空白页面,这可能是由于机器时间机制导致的,此时可能还未发送心跳,加之technical-service-platform控制台默认的又是懒加载模式(可去除该设置),所以最好是我们访问technical-service-platform客户端的/base/api/test接口然后刷新页面即可出现应用。

  1. 访问/base/api/test接口

不停刷新访问/base/api/test接口,观察sentinel-dashboard界面中的实时监控。看到有通过QPS与拒绝QPS的实时监控情况,说明该technical-service-platform客户端已成功接入sentinel-dashboard。

实时监控

5、测试sentinel-dashboard流控规则到Nacos的持久化
(1)确认sentinel-dashboard是否能正确发布流控规则到Nacos
  在sentinel-dashboard针对sentinel客户端的/base/api/test资源节点已经配置了流控规则


流控规则

(2)此时Nacos会对此次流控规则生成持久化配置文件,切换到Nacos-配置列表查看确实存在分组SENTINEL_GROUP下的technical-service-platform-flow-rules配置文件

Nacos持久化存储

点击查看具体内容,发现关键信息都是正确的,说明sentinel-dashboard发布到Nacos通信已经打通

注意:Nacos配置列表出现两个列表配置具体可参考NacosConfigUtil 代码内逻辑
technical-service-platform-flow-rules 存储持久化规则
technical-service-platform-flow-rules-dashboard 存储返回给前端页面展示内容(具体也可以修改前端页面调用方法,返回参数给页面展示内容)

(3)验证流控规则是否生效
  此时我们的QPS阈值为2,也就是说1s之间内我们需要超过访问2次,则会被technical-service-platform限流。不断访问/base/api/test资源节点,观察返回

image.png

备注:针对于返回状态是我在客户端代码自定义返回的格式异常
正常返回Blocked By Sentinel(flow limiting)说明限流规则已经生效

(4)簇点链路流控方法修改
簇点链路列表流控按钮调用方法是默认调用存储在内存中的方法,如下图:

簇点链路

修改如下找到 identity.js 修改 FlowServiceV1为FlowServiceV2


调用内存方法

备注:
至此所有规则方法持久化到Nacos,默认修改配置还是在sentinel-dashboard页面上进行修改,持久化到Nacos配置列表。
如果操作Nacos配置列表修改,需要修改两个配置文件,否则页面显示和实际存储的规则不一样,容易导致规则出错。