ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Sentinel Dashboard-Nacos动态数据源配置

2022-08-23 12:30:09  阅读:259  来源: 互联网

标签: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
来源: https://www.cnblogs.com/wftop1/p/16615726.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有