首页 > 其他分享 >Sentinel Dashboard-Nacos动态数据源配置

Sentinel Dashboard-Nacos动态数据源配置

时间:2022-08-23 12:34:06浏览次数:106  
标签:return 数据源 Nacos entity Dashboard sentinel import alibaba com

Sentinel Dashboard源码中支持push到Sentinel Client(SpringBoot)或动态数据源(Nacos, Zookeeper, Apollo),但目前默认是push到Sentinel Client,推到动态数据源需要稍微改造一下源码

Push模式

img

配置

  • 演示版本
    • SpringBoot:2.2.2.RELEASE
    • Sentinel-DashBoard:1.7.1
    • Nacos-Server:1.2.0

SpringBoot配置

  • maven依赖

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        <version>2.2.0.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
        <version>1.7.1</version>
    </dependency>
    
  • yaml配置

    spring:
      application:
        name: ti-dev
      profiles:
        active: dev
      cloud:
      	# 注册到sentinel dashboard
        sentinel:
          transport:
            dashboard: localhost:8082
          datasource:
            flow: # 名称随意, 标识流量控制
              nacos:
                server-addr: localhost:8848
                namespace: ${spring.profiles.active}
                groupId: SENTINEL_GROUP
                # sentinel dashboard推送的nacos配置的dataId生成规则
                dataId: ${spring.application.name}-flow-rules
                # 规则类型,取值见:
                # org.springframework.cloud.alibaba.sentinel.datasource.RuleType
                rule-type: flow
            param-flow: # 名称随意, 标识热点参数
              nacos:
                server-addr: localhost:8848
                namespace: ${spring.profiles.active}
                groupId: SENTINEL_GROUP
                # sentinel dashboard推送的nacos配置的dataId生成规则
                dataId: ${spring.application.name}-param-rules
                # 规则类型,取值见:
                # org.springframework.cloud.alibaba.sentinel.datasource.RuleType
                rule-type: param-flow
          eager: true # 启动项目是自动注入到sentinel中
          web-content-unify: false
    

Sentinel Dashboard改造

默认情况下,在Sentinel Dashboard控制台修改流控规则之后,经Sentinel Dashboard内部服务,通过Http调用Sentinel Client接口同步rules规则

主要接口与配置类

img

  • 拉取配置接口
    • FlowRuleApiProvider:Http拉取流量控制规则配置
    • FlowRuleNacosProvider:从Nacos拉取流量控制规则配置
    • ParamFlowRuleNacosProvider:需自行改造,从Nacos拉取热点参数规则配置

img

  • DynamicRulePublisher:推送配置接口
    • FlowRuleApiPublisher:Http推送流量控制规则配置
    • FlowRuleNacosPublisher:推送流量控制规则配置到Nacos
    • ParamFlowRuleNacosPublisher:需自行改造,推送热点参数规则配置到Nacos

  • 新建ParamFlowRuleNacosProvider

    package com.alibaba.csp.sentinel.dashboard.rule;
    
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
    import com.alibaba.csp.sentinel.datasource.Converter;
    import com.alibaba.csp.sentinel.util.StringUtil;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    @Component("paramFlowRuleNacosProvider")
    public class ParamFlowRuleNacosProvider implements DynamicRuleProvider<List<ParamFlowRuleEntity>> {
    
        @Autowired
        private ConfigService configService;
        @Autowired
        private Converter<String, List<ParamFlowRuleEntity>> converter;
    
        @Override
        public List<ParamFlowRuleEntity> getRules(String appName) throws Exception {
            String rules = configService.getConfig(appName + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
                    NacosConfigUtil.GROUP_ID, 3000);
            if (StringUtil.isEmpty(rules)) {
                return new ArrayList<>();
            }
            return converter.convert(rules);
        }
    }
    
    

  • 新建ParamFlowRuleNacosPublisher

    package com.alibaba.csp.sentinel.dashboard.rule;
    
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
    import com.alibaba.csp.sentinel.datasource.Converter;
    import com.alibaba.csp.sentinel.util.AssertUtil;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    @Component("paramFlowRuleNacosPublisher")
    public class ParamFlowRuleNacosPublisher implements DynamicRulePublisher<List<ParamFlowRuleEntity>> {
    
        @Autowired
        private ConfigService configService;
        @Autowired
        private Converter<List<ParamFlowRuleEntity>, String> converter;
    
        @Override
        public void publish(String app, List<ParamFlowRuleEntity> rules) throws Exception {
            AssertUtil.notEmpty(app, "app name cannot be empty");
            if (rules == null) {
                return;
            }
            configService.publishConfig(app + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
                    NacosConfigUtil.GROUP_ID, converter.convert(rules));
        }
    }
    
    

  • 修改NacosConfig

    package com.alibaba.csp.sentinel.dashboard.rule;
    
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
    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;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    @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 Converter<List<ParamFlowRuleEntity>, String> paramFlowRuleEntityEncoder() {
            return JSON::toJSONString;
        }
        
    	 // 热点参数规则转换器
        @Bean
        public Converter<String, List<ParamFlowRuleEntity>> paramFlowRuleEntityDecoder() {
            return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);
        }
    
        @Bean
        public ConfigService nacosConfigService() throws Exception {
            Properties properties = new Properties();
            //nacos服务地址
            properties.put(PropertyKeyConst.SERVER_ADDR, "localhost");
            //nacos的namespace
            properties.put(PropertyKeyConst.NAMESPACE, "dev");
            return ConfigFactory.createConfigService(properties);
        }
    }
    

  • 新建com.alibaba.csp.sentinel.dashboard.controller.v2.ParamFlowRuleControllerV2

    package com.alibaba.csp.sentinel.dashboard.controller.v2;
    
    import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
    import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
    import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
    import com.alibaba.csp.sentinel.dashboard.domain.Result;
    import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
    import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
    import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
    import com.alibaba.csp.sentinel.dashboard.util.VersionUtils;
    import com.alibaba.csp.sentinel.slots.block.RuleConstant;
    import com.alibaba.csp.sentinel.util.StringUtil;
    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.*;
    
    import java.util.Date;
    import java.util.List;
    import java.util.Optional;
    import java.util.concurrent.ExecutionException;
    
    /**
     * @author Eric Zhao
     * @since 0.2.1
     */
    @RestController
    @RequestMapping(value = "/v2/paramFlow")
    public class ParamFlowRuleControllerV2 {
    
        private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleControllerV2.class);
    
        @Autowired
        @Qualifier("paramFlowRuleNacosProvider")
        private DynamicRuleProvider<List<ParamFlowRuleEntity>> ruleProvider;
        @Autowired
        @Qualifier("paramFlowRuleNacosPublisher")
        private DynamicRulePublisher<List<ParamFlowRuleEntity>> rulePublisher;
        @Autowired
        private AppManagement appManagement;
        @Autowired
        private RuleRepository<ParamFlowRuleEntity, Long> repository;
    
        private boolean checkIfSupported(String app, String ip, int port) {
            try {
                return Optional.ofNullable(appManagement.getDetailApp(app))
                        .flatMap(e -> e.getMachine(ip, port))
                        .flatMap(m -> VersionUtils.parseVersion(m.getVersion())
                                .map(v -> v.greaterOrEqual(version020)))
                        .orElse(true);
                // If error occurred or cannot retrieve machine info, return true.
            } catch (Exception ex) {
                return true;
            }
        }
    
        @GetMapping("/rules")
        @AuthAction(PrivilegeType.READ_RULE)
        public Result<List<ParamFlowRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,
                                                                            @RequestParam String ip,
                                                                            @RequestParam Integer port) {
            if (StringUtil.isEmpty(app)) {
                return Result.ofFail(-1, "app cannot be null or empty");
            }
            if (StringUtil.isEmpty(ip)) {
                return Result.ofFail(-1, "ip cannot be null or empty");
            }
            if (port == null || port <= 0) {
                return Result.ofFail(-1, "Invalid parameter: port");
            }
            if (!checkIfSupported(app, ip, port)) {
                return unsupportedVersion();
            }
            try {
                List<ParamFlowRuleEntity> rules = ruleProvider.getRules(app);
                if (rules != null && !rules.isEmpty()) {
                    for (ParamFlowRuleEntity entity : rules) {
                        entity.setApp(app);
                        if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) {
                            entity.setId(entity.getClusterConfig().getFlowId());
                        }
                    }
                }
                repository.saveAll(rules);
                return Result.ofSuccess(rules);
            } catch (ExecutionException ex) {
                logger.error("Error when querying parameter flow rules", ex.getCause());
                if (isNotSupported(ex.getCause())) {
                    return unsupportedVersion();
                } else {
                    return Result.ofThrowable(-1, ex.getCause());
                }
            } catch (Throwable throwable) {
                logger.error("Error when querying parameter flow rules", throwable);
                return Result.ofFail(-1, throwable.getMessage());
            }
        }
    
        private boolean isNotSupported(Throwable ex) {
            return ex instanceof CommandNotFoundException;
        }
    
        @PostMapping("/rule")
        @AuthAction(PrivilegeType.WRITE_RULE)
        public Result<ParamFlowRuleEntity> apiAddParamFlowRule(@RequestBody ParamFlowRuleEntity entity) {
            Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);
            if (checkResult != null) {
                return checkResult;
            }
            if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {
                return unsupportedVersion();
            }
            entity.setId(null);
            entity.getRule().setResource(entity.getResource().trim());
            Date date = new Date();
            entity.setGmtCreate(date);
            entity.setGmtModified(date);
            try {
                entity = repository.save(entity);
                publishRules(entity.getApp(), entity.getIp(), entity.getPort());
                return Result.ofSuccess(entity);
            } catch (ExecutionException ex) {
                logger.error("Error when adding new parameter flow rules", ex.getCause());
                if (isNotSupported(ex.getCause())) {
                    return unsupportedVersion();
                } else {
                    return Result.ofThrowable(-1, ex.getCause());
                }
            } catch (Throwable throwable) {
                logger.error("Error when adding new parameter flow rules", throwable);
                return Result.ofFail(-1, throwable.getMessage());
            }
        }
    
        private <R> Result<R> checkEntityInternal(ParamFlowRuleEntity entity) {
            if (entity == null) {
                return Result.ofFail(-1, "bad rule body");
            }
            if (StringUtil.isBlank(entity.getApp())) {
                return Result.ofFail(-1, "app can't be null or empty");
            }
            if (StringUtil.isBlank(entity.getIp())) {
                return Result.ofFail(-1, "ip can't be null or empty");
            }
            if (entity.getPort() == null || entity.getPort() <= 0) {
                return Result.ofFail(-1, "port can't be null");
            }
            if (entity.getRule() == null) {
                return Result.ofFail(-1, "rule can't be null");
            }
            if (StringUtil.isBlank(entity.getResource())) {
                return Result.ofFail(-1, "resource name cannot be null or empty");
            }
            if (entity.getCount() < 0) {
                return Result.ofFail(-1, "count should be valid");
            }
            if (entity.getGrade() != RuleConstant.FLOW_GRADE_QPS) {
                return Result.ofFail(-1, "Unknown mode (blockGrade) for parameter flow control");
            }
            if (entity.getParamIdx() == null || entity.getParamIdx() < 0) {
                return Result.ofFail(-1, "paramIdx should be valid");
            }
            if (entity.getDurationInSec() <= 0) {
                return Result.ofFail(-1, "durationInSec should be valid");
            }
            if (entity.getControlBehavior() < 0) {
                return Result.ofFail(-1, "controlBehavior should be valid");
            }
            return null;
        }
    
        @PutMapping("/rule/{id}")
        @AuthAction(PrivilegeType.WRITE_RULE)
        public Result<ParamFlowRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id,
                                                                  @RequestBody ParamFlowRuleEntity entity) {
            if (id == null || id <= 0) {
                return Result.ofFail(-1, "Invalid id");
            }
            ParamFlowRuleEntity oldEntity = repository.findById(id);
            if (oldEntity == null) {
                return Result.ofFail(-1, "id " + id + " does not exist");
            }
    
            Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);
            if (checkResult != null) {
                return checkResult;
            }
            if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {
                return unsupportedVersion();
            }
            entity.setId(id);
            Date date = new Date();
            entity.setGmtCreate(oldEntity.getGmtCreate());
            entity.setGmtModified(date);
            try {
                entity = repository.save(entity);
                publishRules(entity.getApp(), entity.getIp(), entity.getPort());
                return Result.ofSuccess(entity);
            } catch (ExecutionException ex) {
                logger.error("Error when updating parameter flow rules, id=" + id, ex.getCause());
                if (isNotSupported(ex.getCause())) {
                    return unsupportedVersion();
                } else {
                    return Result.ofThrowable(-1, ex.getCause());
                }
            } catch (Throwable throwable) {
                logger.error("Error when updating parameter flow rules, id=" + id, throwable);
                return Result.ofFail(-1, throwable.getMessage());
            }
        }
    
        @DeleteMapping("/rule/{id}")
        @AuthAction(PrivilegeType.DELETE_RULE)
        public Result<Long> apiDeleteRule(@PathVariable("id") Long id) {
            if (id == null) {
                return Result.ofFail(-1, "id cannot be null");
            }
            ParamFlowRuleEntity oldEntity = repository.findById(id);
            if (oldEntity == null) {
                return Result.ofSuccess(null);
            }
    
            try {
                repository.delete(id);
                publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort());
                return Result.ofSuccess(id);
            } catch (ExecutionException ex) {
                logger.error("Error when deleting parameter flow rules", ex.getCause());
                if (isNotSupported(ex.getCause())) {
                    return unsupportedVersion();
                } else {
                    return Result.ofThrowable(-1, ex.getCause());
                }
            } catch (Throwable throwable) {
                logger.error("Error when deleting parameter flow rules", throwable);
                return Result.ofFail(-1, throwable.getMessage());
            }
        }
    
        private void publishRules(String app, String ip, Integer port) throws Exception {
            List<ParamFlowRuleEntity> rules = repository.findAllByApp(app);
            rulePublisher.publish(app, rules);
        }
    
        private <R> Result<R> unsupportedVersion() {
            return Result.ofFail(4041,
                    "Sentinel client not supported for parameter flow control (unsupported version or dependency absent)");
        }
    
        private final SentinelVersion version020 = new SentinelVersion().setMinorVersion(2);
    }
    

  • 修改Sentinel Dashboard前端调用的接口。找到resources\dist\js\app.js,搜索/paramFlow/rules,将/paramFlow/rules改为/v2/paramFlow/rules

    img


  • 修改ParamFlowRuleEntity

    package com.alibaba.csp.sentinel.dashboard.datasource.entity.rule;
    
    import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig;
    import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
    import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
    import com.alibaba.csp.sentinel.util.AssertUtil;
    import com.alibaba.fastjson.annotation.JSONField;
    import com.fasterxml.jackson.annotation.JsonIgnore;
    
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 0.2.1
     */
    public class ParamFlowRuleEntity extends AbstractRuleEntity<ParamFlowRule> {
    
        public ParamFlowRuleEntity() {
        }
    
        public ParamFlowRuleEntity(ParamFlowRule rule) {
            AssertUtil.notNull(rule, "Authority rule should not be null");
            this.rule = rule;
        }
    
        public static ParamFlowRuleEntity fromAuthorityRule(String app, String ip, Integer port, ParamFlowRule rule) {
            ParamFlowRuleEntity entity = new ParamFlowRuleEntity(rule);
            entity.setApp(app);
            entity.setIp(ip);
            entity.setPort(port);
            return entity;
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public String getLimitApp() {
            return rule.getLimitApp();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public String getResource() {
            return rule.getResource();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public int getGrade() {
            return rule.getGrade();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public Integer getParamIdx() {
            return rule.getParamIdx();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public double getCount() {
            return rule.getCount();
        }
    
        @JsonIgnore
        @JSONField(serialize = false)
        public List<ParamFlowItem> getParamFlowItemList() {
            return rule.getParamFlowItemList();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public int getControlBehavior() {
            return rule.getControlBehavior();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public int getMaxQueueingTimeMs() {
            return rule.getMaxQueueingTimeMs();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public int getBurstCount() {
            return rule.getBurstCount();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public long getDurationInSec() {
            return rule.getDurationInSec();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public boolean isClusterMode() {
            return rule.isClusterMode();
        }
    
        //    @JsonIgnore
    //    @JSONField(serialize = false)
        public ParamFlowClusterConfig getClusterConfig() {
            return rule.getClusterConfig();
        }
    }
    
    

参考:

问题:

  1. 关于集群token server独立部署高可用方案

标签:return,数据源,Nacos,entity,Dashboard,sentinel,import,alibaba,com
From: https://www.cnblogs.com/wftop1/p/16615726.html

相关文章

  • nacos指定jdk版本启动
    问题描述:nacos运行的官方jdk环境为jdk1.8+,而我们有两台服务器的jdk是1.7,因为发布着其他应用,不好升级jdk,故需要在启动nacos时指定jdk为1.8解决方案:修改nacos启动脚本,指定......
  • Nacos安装教程
    1、下载安装gitclonehttps://gitee.com/mirrors/Nacos.gitcdnacos/#如果本地没有mvn命令,或者是编译过程中报错,缺少jdk,需要全局配置mvn命令,vim~/.bash_prof......
  • SpringBoot读取.yml配置文件最常见的两种方式-源码及其在nacos的应用
    一、前言我们在开发中会经常遇到一些可能会变的值,比如数据库的密码,一些关键链接的配置等等。都需要我们写在配置文件中,这样可以把这些配置文件放到nacos上进行管理,修改na......
  • Nacos
    Nacos学习启动与关闭找到Nacos安装的bin目录输入cmd命令行打开命令窗口输入指令startup.cmd-mstandalone,standalone指的是将Nacos以单机模式运行,非集群模式......
  • Nacos的下载与启动
    参考链接:https://blog.csdn.net/Passer_hua/article/details/125941453一、下载github官网:https://github.com/alibaba/nacos/releases由于github官网国内访问比较慢,于......
  • MybatisPlus——实现多数据源操作
    多数据源适用:一般工作时候会有多个数据库,每个库对应不同的业务数据。程序如果每次数据都访问同一个数据库,该数据库压力很大访问会很慢。官方文档:https://baomidou.com/......
  • 部署 prometheus + grafana + nacos
    linux环境部署prometheus+grafana+nacos对微服务监控IT学习道场 IT学习道场 2022-06-0619:34 发表于浙江收录于合集#IT学习道场靓文集锦34个自定义的prome......
  • Nacos和Eureka的不同
    从功能上来说Nacos的功能比Eureka更强大,Nacos还可以作为配置中心来使用。作为服务注册中心来说,Eureka和Nacos都支持心跳检测来确保服务正常。不同之处在于Eureka对服务提......
  • 关于devreport 使用报表模版.repx,数据源和模版文件关系
    随着用户需求不断增加,固定的report表内的字段.不能满足用户需求.于是要给用户加上报表的设计功能.这样在展示的时候更有灵活性,也不用每个改动都找程序人员不带设计......
  • Nacos入门
    1.在父项目中引入SpringCloudAlibaba依赖<dependencyManagement><dependencies><dependency><groupId>org.springframework.clo......