Sentinel集成Nacos实现动态流控规则

Sentinel的理念是只需要开发者关注资源的定义,它默认会对资源进行流控。当然,我们还是需要对定义的资源设置流控规则,主要有两种方式:

  • 通过FlowRuleManager.loadRules()手动加载流控规则。
  • 在Sentinel Dashboard上针对资源动态创建流控规则。

针对第一种方式,如果接入Sentinel Dashboard,那么同样支持动态修改流控规则,但是基于Sentinel Dashboard所配置的流控规则,都是保存在内存中的,一旦应用重启,这些规则都会被清除。为了解决这个问题,Sentinel提供了动态数据源支持。

目前,Sentinel支持Consul、Zookeeper、Redis、Nacos、Apollo、etcd等数据源的扩展,接下来通过一个案例展示Spring Cloud Sentinel集成Nacos实现动态流控规则,步骤如下:

  1. 添加Nacos数据源的依赖包
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.7.0</version>
</dependency>
  1. 创建一个REST接口,用于测试
@RestController
public class DynamicController {

    @GetMapping("/dynamic")
    public String dynamic() {
        return "Hello Dynamic Rules";
    }
}
  1. 在application.yml文件中添加数据源配置
spring:
  application:
    name: sentinel-spring-cloud-demo
  cloud:
    sentinel:
      transport:
        dashboard: 192.168.56.1:7777
      datasource:
        - nacos:
            server-addr: 192.168.56.1:8848
            data-id: ${spring.application.name}-sentinel
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow

部分配置说明如下:

  • datasource: 目前支持redis、apollo、zk、file、nacos,选什么类型的数据源就配置相应的key即可。
  • data-id:可以设置成${spring.application.name},方便区分不同应用的配置。
  • rule-type:表示数据源中规则属于哪种类型,如flow、degrade、param-flow、gw-flow等。
  • data-type:指配置项的内容格式,Spring Cloud Alibaba Sentinel提供了JSON和XML两种格式,如需要自定义,则可以将值配置为custom,并配置converter-class指向converter类。
  1. 登陆Nacos控制台,创建流控配置规则:


    在这里插入图片描述
  2. 登陆Sentinel Dashboard,找到执行项目名称菜单下的“流控规则”,就可以看到在Nacos上所配置的流控规则已经被加载了。


    在这里插入图片描述
  3. 当我们在Nacos的控制台上修改流控规则后,可以同步的在Sentinel Dashboard上看到流控规则的变化。

那么问题就来了,Nacos其实应该作为一个流控规则的持久化平台,正常的操作过程应该是在Sentinel Dashboard上修改流控规则,然后同步到Nacos上,但是遗憾的是,目前Sentinel Dashboard并不支持该功能。

所以,Nacos名义上是"Datasource",实际上充当的仍然是配置中心的角色,开发者可以在Nacos控制台上动态修改流控规则并实现规则同步。在实际开发中,很难避免在不清楚情况的情况下,部分开发者使用Sentinel Dashboard来管理流控规则,部分开发者通过Nacos来管理流控规则,这可能导致非常严重的问题。

要想使用Sentinel Dashboard来统一管理流控规则并同步到Nacos上,我们可以自己来实现。

Sentinel Dashboard集成Nacos实现规则同步

Sentinel Dashboard的流控规则下的所有操作,都会调用Sentinel-Dashboard源码中的FlowControllerV1类,这个类中包含流控规则本地化 的CRUD操作。

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

@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {

    private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);

    @Autowired
    private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;

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

FlowControllerV2依赖以下两个非常重要的类:

  • DynamicRuleProvider: 动态规则的拉取,从指定数据源中获取流控规则后在Sentinel Dashboard中展示。
  • DynamicRulePublisher: 动态规则的发布,将在Sentinel Dashboard中修改的规则同步到指定数据源中。

我们可以扩展这两个类,然后集成Nacos来实现Sentinel Dashboard规则的同步。

Sentinel Dashboard源码修改

修改Sentinel Dashboard的源码,具体实现步骤如下:

  1. 在GitHub中下载Sentinel Dashboard 1.7.1的源码。
  2. 使用IDEA工具打开sentinel-dashboard工程。
  3. 在pom.xml中把sentinel-datasource-nacos依赖的<scope>注释掉。
<!-- for Nacos rule publisher sample -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!--<scope>test</scope>-->
</dependency>
  1. 修改resources/app/scripts/directives/sidebar/sidebar.html文件中下面这段代码,将dashboard.flowV1改成dashboard.flow,也就是去掉V1。修改之后,会调用FlowControllerV2中的接口。
<li ui-sref-active="active" ng-if="!entry.isGateway">
  <!--<a ui-sref="dashboard.flowV1({app: entry.app})">-->
  <a ui-sref="dashboard.flow({app: entry.app})">
    <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
</li>
  1. 在com.alibaba.csp.sentinel.dashboard.rule包中创建一个Nacos包,并创建一个类用来加载外部化配置。
@ConfigurationProperties(prefix = "sentinel.nacos")
public class NacosPropertiesConfiguration {

    private String serverAddr;
    private String dataId;
    private String groupId = "DEFAULT_GROUP";
    private String namespace;
    // 省略get/set方法
}
  1. 创建一个Nacos配置类NacosConfiguration
@EnableConfigurationProperties(NacosPropertiesConfiguration.class)
@Configuration
public class NacosConfiguration {

    @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(NacosPropertiesConfiguration nacosPropertiesConfiguration) throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfiguration.getServerAddr());
        properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfiguration.getNamespace());
        return ConfigFactory.createConfigService(properties);
    }

}
  1. 创建一个常量类NacosConstants,分别表示默认的GROUP_ID和DATA_ID的后缀
public class NacosConstants {

    public static final String DATA_ID_POSTFIX = "-sentinel-flow";
    public static final String GROUP_ID = "DEFAULT_GROUP";
}
  1. 实现动态从Nacos配置中心获取流控规则
@Service
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    private static final Logger logger = LoggerFactory.getLogger(FlowRuleNacosProvider.class);

    @Autowired
    private NacosPropertiesConfiguration nacosPropertiesConfiguration;

    @Autowired
    private ConfigService configService;

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

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        String dataId = new StringBuilder(appName).append(NacosConstants.DATA_ID_POSTFIX).toString();
        String rules = configService.getConfig(dataId, nacosPropertiesConfiguration.getGroupId(), 3000);
        logger.info("pull flow rule from Nacos Config: {}", rules);
        if (StringUtils.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
  1. 创建一个流控规则发布类,在Sentinel Dashboard上修改完配置后,需要调用该发布方法将数据持久化到Nacos中。
@Service
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private NacosPropertiesConfiguration nacosPropertiesConfiguration;

    @Autowired
    private ConfigService configService;

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

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app cannot be empty");
        if (rules == null) {
            return;
        }
        String dataId = new StringBuilder(app).append(NacosConstants.DATA_ID_POSTFIX).toString();
        configService.publishConfig(dataId, nacosPropertiesConfiguration.getGroupId(), converter.convert(rules));
    }
}
  1. 修改FlowControllerV2类,将上面配置的两个类注入进来,表示规则的拉取和规则的发布统一用我们前面定义的两个实例,然后将FlowControllerV2这个类中的代码覆盖FlowControllerV1的代码
@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {

    private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);

    @Autowired
    private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;

    @Autowired
    @Qualifier("flowRuleNacosProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("flowRuleNacosPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
  1. 在application.properties文件中添加Nacos服务端的配置信息
sentinel.nacos.serverAddr=192.168.56.1:8848
sentinel.nacos.namespacec=
sentinel.nacos.group-id=DEFAULT_GROUP
  1. 使用下面命令将代码打包成一个 fat jar,然后启动。
mvn clean package
Sentinel Dashboard规则数据同步

对于应用程序来说,需要改动的地方比较少,只需要注意配置文件中的data-id的命名要以-sentinel-flow结尾即可,因为在sentinel dashboard中我们写了一个固定的后缀。

spring:
  application:
    name: spring-cloud-sentinel-dynamic
  cloud:
    sentinel:
      transport:
        dashboard: 192.168.56.1:7777
      datasource:
        - nacos:
            server-addr: 192.168.56.1:8848
            data-id: ${spring.application.name}-sentinel-flow
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow
  1. 登陆Sentinel Dashboard,进入“流控规则”,然后针对指定的资源创建流控规则。
  2. 进入Nacos控制台,就可以看到在Sentinel Dashboard中配置的流控规则。


    在这里插入图片描述
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271