【第31章】Spring Cloud之Sentinel控制台推送规则到Nacos

ops/2024/10/18 0:36:31/

文章目录

  • 前言
  • 一、下载源码
    • 1. 下载源码
  • 二、规则配置
    • 1. Nacos适配
      • 1.1 使用数据源
      • 1.2 复制官方案例
      • 1.3 动态规则配置中心
    • 2. 前端路由配置
    • 3. 提示
    • 4. 编译和启动
  • 三、测试
    • 1. 修改前
    • 2. 修改后
  • 总结


前言

前面我们已经完成了通过nacos存储提供者流控配置文件,下面我们来接入Sentinel控制台推送规则到Nacos数据源。


一、下载源码

这里我们需要修改sentinel-dashboard源码,并重新编译打包,后续启动使用新包。

1. 下载源码

下载地址
在这里插入图片描述

二、规则配置

要通过 Sentinel 控制台配置集群流控规则,需要对控制台进行改造。我们提供了相应的接口进行适配。

从 Sentinel 1.4.0 开始,我们抽取出了接口用于向远程配置中心推送规则以及拉取规则:

  • DynamicRuleProvider: 拉取规则
  • DynamicRulePublisher: 推送规则

对于集群限流的场景,由于每个集群限流规则都需要唯一的 flowId,因此我们建议所有的规则配置都通过动态规则源进行管理,并在统一的地方生成集群限流规则。

我们提供了新版的流控规则页面,可以针对应用维度推送规则,对于集群限流规则可以自动生成 flowId。用户只需实现 DynamicRuleProvider 和 DynamicRulePublisher 接口,即可实现应用维度推送(URL: /v2/flow)。

1. Nacos适配

我们提供了 Nacos、ZooKeeper 和 Apollo 的推送和拉取规则实现示例(位于 test 目录下)。以 Nacos 为例,若希望使用 Nacos 作为动态规则配置中心,用户可以提取出相关的类,然后只需在 FlowControllerV2 中指定对应的 bean 即可开启 Nacos 适配。

1.1 使用数据源

        <!-- for Nacos rule publisher sample --><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId>
<!--            <scope>test</scope>--></dependency>

1.2 复制官方案例

1.3 动态规则配置中心

使用Nacos推送和拉取规则

FlowControllerV2

java">/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.csp.sentinel.dashboard.controller.v2;import java.util.Date;
import java.util.List;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.util.StringUtil;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.domain.Result;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** Flow rule controller (v2).** @author Eric Zhao* @since 1.4.0*/
@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);@Autowiredprivate InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;//    @Autowired
//    @Qualifier("flowRuleDefaultProvider")
//    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
//    @Autowired
//    @Qualifier("flowRuleDefaultPublisher")
//    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;@Autowired@Qualifier("flowRuleNacosProvider")private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;@Autowired@Qualifier("flowRuleNacosPublisher")private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;@GetMapping("/rules")@AuthAction(PrivilegeType.READ_RULE)public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app) {if (StringUtil.isEmpty(app)) {return Result.ofFail(-1, "app can't be null or empty");}try {List<FlowRuleEntity> rules = ruleProvider.getRules(app);if (rules != null && !rules.isEmpty()) {for (FlowRuleEntity entity : rules) {entity.setApp(app);if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) {entity.setId(entity.getClusterConfig().getFlowId());}}}rules = repository.saveAll(rules);return Result.ofSuccess(rules);} catch (Throwable throwable) {logger.error("Error when querying flow rules", throwable);return Result.ofThrowable(-1, throwable);}}private <R> Result<R> checkEntityInternal(FlowRuleEntity entity) {if (entity == null) {return Result.ofFail(-1, "invalid body");}if (StringUtil.isBlank(entity.getApp())) {return Result.ofFail(-1, "app can't be null or empty");}if (StringUtil.isBlank(entity.getLimitApp())) {return Result.ofFail(-1, "limitApp can't be null or empty");}if (StringUtil.isBlank(entity.getResource())) {return Result.ofFail(-1, "resource can't be null or empty");}if (entity.getGrade() == null) {return Result.ofFail(-1, "grade can't be null");}if (entity.getGrade() != 0 && entity.getGrade() != 1) {return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got");}if (entity.getCount() == null || entity.getCount() < 0) {return Result.ofFail(-1, "count should be at lease zero");}if (entity.getStrategy() == null) {return Result.ofFail(-1, "strategy can't be null");}if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) {return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0");}if (entity.getControlBehavior() == null) {return Result.ofFail(-1, "controlBehavior can't be null");}int controlBehavior = entity.getControlBehavior();if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) {return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1");}if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) {return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2");}if (entity.isClusterMode() && entity.getClusterConfig() == null) {return Result.ofFail(-1, "cluster config should be valid");}return null;}@PostMapping("/rule")@AuthAction(value = AuthService.PrivilegeType.WRITE_RULE)public Result<FlowRuleEntity> apiAddFlowRule(@RequestBody FlowRuleEntity entity) {Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}entity.setId(null);Date date = new Date();entity.setGmtCreate(date);entity.setGmtModified(date);entity.setLimitApp(entity.getLimitApp().trim());entity.setResource(entity.getResource().trim());try {entity = repository.save(entity);publishRules(entity.getApp());} catch (Throwable throwable) {logger.error("Failed to add flow rule", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(entity);}@PutMapping("/rule/{id}")@AuthAction(AuthService.PrivilegeType.WRITE_RULE)public Result<FlowRuleEntity> apiUpdateFlowRule(@PathVariable("id") Long id,@RequestBody FlowRuleEntity entity) {if (id == null || id <= 0) {return Result.ofFail(-1, "Invalid id");}FlowRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofFail(-1, "id " + id + " does not exist");}if (entity == null) {return Result.ofFail(-1, "invalid body");}entity.setApp(oldEntity.getApp());entity.setIp(oldEntity.getIp());entity.setPort(oldEntity.getPort());Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}entity.setId(id);Date date = new Date();entity.setGmtCreate(oldEntity.getGmtCreate());entity.setGmtModified(date);try {entity = repository.save(entity);if (entity == null) {return Result.ofFail(-1, "save entity fail");}publishRules(oldEntity.getApp());} catch (Throwable throwable) {logger.error("Failed to update flow rule", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(entity);}@DeleteMapping("/rule/{id}")@AuthAction(PrivilegeType.DELETE_RULE)public Result<Long> apiDeleteRule(@PathVariable("id") Long id) {if (id == null || id <= 0) {return Result.ofFail(-1, "Invalid id");}FlowRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);publishRules(oldEntity.getApp());} catch (Exception e) {return Result.ofFail(-1, e.getMessage());}return Result.ofSuccess(id);}private void publishRules(/*@NonNull*/ String app) throws Exception {List<FlowRuleEntity> rules = repository.findAllByApp(app);rulePublisher.publish(app, rules);}
}

NacosConfig

java">/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
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;/***定义nacos连接信息*/
@Configuration
public class NacosConfig {@Beanpublic Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {return s -> JSON.parseArray(s, FlowRuleEntity.class);}@Beanpublic ConfigService nacosConfigService() throws Exception {Properties properties = new Properties();properties.setProperty("serverAddr","localhost");properties.setProperty("namespace","dev");properties.setProperty("username","admin");properties.setProperty("password","admin");return ConfigFactory.createConfigService(properties);}
}

NacosConfigUtil

java">/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.csp.sentinel.dashboard.rule.nacos;/***主要是定义GROUP_ID和FLOW_DATA_ID_POSTFIX*/
public final class NacosConfigUtil {public static final String GROUP_ID = "SENTINEL_GROUP";public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules.json";public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";/*** cc for `cluster-client`*/public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config";/*** cs for `cluster-server`*/public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config";public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config";public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set";private NacosConfigUtil() {}
}

2. 前端路由配置

前端页面需要手动切换,或者修改前端路由配置(sidebar.html 流控规则路由从 dashboard.flowV1 改成 dashboard.flow 即可,注意簇点链路页面对话框需要自行改造)。
在这里插入图片描述

3. 提示

默认 Nacos 适配的 dataId 和 groupId 约定如下:

  • groupId: SENTINEL_GROUP
  • 流控规则 dataId: {appName}-flow-rules,比如应用名为 appA,则 dataId 为 appA-flow-rules

用户可以在 NacosConfigUtil 修改对应的 groupId 和 dataId postfix。用户可以在 NacosConfig 配置对应的 Converter,默认已提供 FlowRuleEntity 的 decoder 和 encoder。

注意:接入端也需要注册对应的动态规则源,参考 集群流控规则配置文档。

4. 编译和启动

mvn clean package

三、测试

1. 修改前

第一次懒加载请求会全部通过
在这里插入图片描述

在这里插入图片描述

2. 修改后

在这里插入图片描述
在这里插入图片描述

控制台推送过来的没有样式,先将就吧
在这里插入图片描述

请求阈值升为6,请求全部通过,修改生效,符合预期
在这里插入图片描述


总结

回到顶部

使用Sentinel控制台+应用程序+Nacos,优点很明显,可视化配置+动态修改+持久化存储;
使用控制台之后也有个缺点,簇点链路和流控规则不互通了,官方给的说法是注意簇点链路页面对话框需要自行改造,最简单的办法就是原来的路由也放开,一块用呗!没试,大家自己试吧~

其实应用程序+Nacos完全够用,Sentinel控制台完全可以作为临时控制和调整使用,具体使用还是以配置文件为主,控制台为辅。

我调整后的这一版已上传附件,欢迎大家下载使用!


http://www.ppmy.cn/ops/109759.html

相关文章

干货分享|分享一款完全免费的PDF工具箱 PDF24

PDF 24介绍: PDF 24是一个全面的PDF工具包&#xff0c;旨在简化和优化PDF文件的处理和管理。它提供了广泛的功能和工具&#xff0c;包括创建、编辑、转换、合并、拆分、加密、解密、压缩、优化、签名、水印等。 主要特点&#xff1a;完全免费&#xff0c;轻量便捷&#xff0c;…

SpringBoot和Mybatis框架怎么防止SQL注入

在 Spring Boot 和 MyBatis 中&#xff0c;防止 SQL 注入的主要方法包括&#xff1a; 1.使用 MyBatis 的动态 SQL MyBatis 提供了安全构建 SQL 查询的方式&#xff0c;推荐使用动态 SQL 标签&#xff08;如 <if>、<choose>、<foreach> 等&#xff09;构建查…

HarmonyOS开发实战( Beta5.0)DevEco Device Tool开发环境搭建实践

通常在嵌入式开发中&#xff0c;很多开发者习惯于使用Windows进行代码的编辑&#xff0c;比如使用Windows的Visual Studio Code进行OpenHarmony代码的开发。但当前阶段&#xff0c;大部分的开发板源码还不支持在Windows环境下进行编译&#xff0c;如Hi3516、Hi3518系列开发板。…

[C++11#48][智能指针] RAII原则 | 智能指针的类型 | 模拟实现 | shared_ptr | 解决循环引用

目录 一.引入 1. 为什么需要智能指针&#xff1f; 2. 什么是内存泄漏&#xff1f; 内存泄漏分类 3.回忆 this 二. 原理 1. RAII 资源获取即初始化 2.像指针一样 三. 使用 1. 问题&#xff1a; string 的浅拷贝 2.解决 auto_ptr 自定义 auto_ptr unique_ptr - 独占…

【Dart 教程系列第 50 篇】在 Flutter 项目的国际化多语言中,如何根据翻译提供的多语言文档表格,快速生成不同语言的内容

这是【Dart 教程系列第 50 篇】&#xff0c;如果觉得有用的话&#xff0c;欢迎关注专栏。 博文当前所用 Flutter SDK&#xff1a;3.22.1、Dart SDK&#xff1a;3.4.1 文章目录 一&#xff1a;问题描述二&#xff1a;解决方案三&#xff1a;完整代码 一&#xff1a;问题描述 在…

Python精选200Tips:131-135

Put the final touches on something 131 python-docx - 创建和修改文档的库基本功能主要类和方法示例132 NetworkX - 网络分析和图论工具示例1: 小世界网络示例2:社交网络分析示例3:交通网络分析133 Statsmodels - 统计建模和计量经济学示例1:线性回归的残差分析示例2:年龄、性…

组织应在其网络安全策略中考虑MLSecOps吗?

随着越来越多的组织拥抱人工智能 (AI) 和机器学习 (ML) 来优化操作并获得竞争优势&#xff0c;关于如何最好地保障这一强大技术的安全性的问题也日益受到关注。其中的核心是用于训练ML模型的数据&#xff0c;这对模型的行为和性能有着根本影响。因此&#xff0c;组织需要密切关…

计算机视觉工程师所需的基本技能

一、编程技能 熟练掌握编程语言 Python&#xff1a;在计算机视觉领域广泛应用&#xff0c;有丰富的库如 OpenCV、TensorFlow、PyTorch 等&#xff0c;方便进行算法实现和模型开发。 C&#xff1a;运行效率高&#xff0c;适用于对性能要求严格的计算机视觉应用。 数据结构与算…