官网 git: Sentinel
改造后源码:dashboard-nacos分支
sentinel版本:1.8.1
改造的规则有:授权规则、降级规则、流控规则、热点规则、系统规则

改造过程

修改pom.xml
  • 下载源码,idea打开。
  • 找到sentinel-dashboard这个项目
  • 在该项目下的pom.xml文件中找到:
        <!-- 将<scope>test</scope>注释掉。 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
<!--            <scope>test</scope>-->
        </dependency>
修改application.properties配置文件

添加如下配置:

# nacos settings
sentinel.datasource.nacos.enable=true
sentinel.datasource.nacos.server-addr=10.168.1.99:8848
sentinel.datasource.nacos.namespace=dev
修改代码,以流控规则为例
在java->com.alibaba.csp.sentinel.dashboard下添加nacos包
添加NacosConfigUtil.java

注意 GROUP_ID 、xx_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";
    public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
    public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
    public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";
    public static final String SYSTEM_DATA_ID_POSTFIX = "-system-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() {}
}
在nacos新建三个包
  • convert
  • provider
  • publisher

  1. 在publisher包下编写FlowRuleNacosPublisher 规则发布
    FlowRuleNacosPublisher.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.nacos.publisher;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.dashboard.nacos.convert.FlowRuleConvert;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
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("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private FlowRuleConvert converter;

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        boolean success = configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, converter.convert(rules));
        if(!success){
            throw new RuntimeException("publish to nacos fail");
        }
    }
}

  1. 在convert包下编写FlowRuleConvert转换类
    FlowRuleConvert.java
package com.alibaba.csp.sentinel.dashboard.nacos.convert;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.fastjson.JSON;

import java.util.ArrayList;
import java.util.List;

/**
 * @author T6001270
 * @since 2020/9/30 4:18
 */
public class FlowRuleConvert implements Converter<List<FlowRuleEntity>, String> {

    @Override
    public String convert(List<FlowRuleEntity> flowRuleEntities) {
        if(flowRuleEntities==null){
            return null;
        }
        List<FlowRule> flowRules = new ArrayList<>();
        for (FlowRuleEntity entity : flowRuleEntities) {
            FlowRule rule = new FlowRule();
            rule.setLimitApp(entity.getLimitApp());
            rule.setResource(entity.getResource());
            if(entity.getGmtCreate()!=null){
                rule.setGrade(entity.getGrade());
            }
            if(entity.getCount()!=null){
                rule.setCount(entity.getCount());
            }
            if(entity.getStrategy()!=null){
                rule.setStrategy(entity.getStrategy());
            }
            rule.setRefResource(entity.getRefResource());
            if(entity.getControlBehavior()!=null){
                rule.setControlBehavior(entity.getControlBehavior());
            }
            if(entity.getWarmUpPeriodSec()!=null){
                rule.setWarmUpPeriodSec(entity.getWarmUpPeriodSec());
            }
            if(entity.getMaxQueueingTimeMs()!=null){
                rule.setMaxQueueingTimeMs(entity.getMaxQueueingTimeMs());
            }
            rule.setClusterMode(entity.isClusterMode());
            rule.setClusterConfig(entity.getClusterConfig());

            flowRules.add(rule);
        }
        return JSON.toJSONString(flowRules,true);
    }
}

  1. 在provider包下编写FlowRuleNacosProvider获取规则
    FlowRuleNacosProvider.java
package com.alibaba.csp.sentinel.dashboard.nacos.provider;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
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 T6001270
 * @since 2020/9/30 4:18
 */
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

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

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
  1. 在nacos包下编写NacosConfig
package com.alibaba.csp.sentinel.dashboard.nacos;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.*;
import com.alibaba.csp.sentinel.dashboard.nacos.convert.*;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
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.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;

/**
 * @author T6001270
 * @since 2020/9/30 4:18
 */
@Configuration
public class NacosConfig {

    @Value("${sentinel.datasource.nacos.server-addr:localhost:8848}")
    private String serverAddr;

    @Value("${sentinel.datasource.nacos.enable:false}")
    private boolean enable;
    @Value("${sentinel.datasource.nacos.namespace:public}")
    private String namespace;

    @Bean
    public FlowRuleConvert flowRuleEntityEncoder() {
        return new FlowRuleConvert();
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public ParamFlowRuleConvert paramFlowRuleEntityEncoder() {
        return new ParamFlowRuleConvert();
    }

    @Bean
    public Converter<String, List<ParamFlowRuleEntity>> paramFlowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, ParamFlowRule.class).stream().filter(Objects::nonNull).map(ParamFlowRuleEntity::new).collect(Collectors.toList());
    }


    @Bean
    public DegradeRuleConvert degradeRuleEntityEncoder() {
        return new DegradeRuleConvert();
    }

    @Bean
    public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
        return s -> JSON.parseArray(s, DegradeRuleEntity.class);
    }

    @Bean
    public AuthorityRuleConvert authorityRuleConvert() {
        return new AuthorityRuleConvert();
    }

    @Bean
    public Converter<String, List<AuthorityRuleEntity>> authorityRuleEntityDecoder() {
        return s -> JSON.parseArray(s, AuthorityRule.class).stream().filter(Objects::nonNull).map(AuthorityRuleEntity::new).collect(Collectors.toList());
    }

    @Bean
    public SystemRuleConvert systemRuleConvert() {
        return new SystemRuleConvert();
    }

    @Bean
    public Converter<String, List<SystemRuleEntity>> systemRuleEntityDecoder() {
        return s -> JSON.parseArray(s, SystemRuleEntity.class);
    }

    @Bean
    public ConfigService nacosConfigService() throws Exception {
        Properties properties = new Properties();
        properties.put("serverAddr", serverAddr);
        properties.put(PropertyKeyConst.NAMESPACE, namespace);
        return ConfigFactory.createConfigService(properties);
    }


    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }
}
  1. 修改FlowControllerV1
    /* 添加rulePublisher和ruleProvider */
    @Autowired
    @Qualifier("flowRuleNacosProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("flowRuleNacosPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
/**
     * 改造publishRules方法,调用该方法的代码请修改参数和返回值
     * @param app 客户端名称
     * @throws Exception
     */
    private void publishRules(/*@NonNull*/ String app) throws Exception {
        List<FlowRuleEntity> rules = repository.findAllByApp(app);
        if(nacosConfig.isEnable()){
            rulePublisher.publish(app,rules);
        }
    }
    /**
     * 修改查询规则列表接口
     * @param app 应用名称
     * @param ip 规则提供者ip
     * @param port 端口
     * @return
     */
    @GetMapping("/rules")
    @AuthAction(PrivilegeType.READ_RULE)
    public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app,
                                                             @RequestParam String ip,
                                                             @RequestParam Integer port) {

        if (StringUtil.isEmpty(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        if (StringUtil.isEmpty(ip)) {
            return Result.ofFail(-1, "ip can't be null or empty");
        }
        if (port == null) {
            return Result.ofFail(-1, "port can't be null");
        }
        try {
            /*修改从提供者获取规则方式 */
            List<FlowRuleEntity> rules = ruleProvider.getRules(app);
            if (rules != null && !rules.isEmpty()) {
                for (FlowRuleEntity entity : rules) {
                    entity.setApp(app);
                    entity.setPort(port);
                    entity.setIp(ip);
                    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);
        }
    }

其他规则修改步骤类似,具体代码参考dashboard-nacos分支

  1. 客户端配置
spring:
  cloud:
    # 配置Sentinel流控
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: 127.0.0.1:8080
        #默认8719端口,如果被占用会向上扫描。
        port: 8719
      datasource:
      # 分组与命名空间和服务端保持一致
        flow:
          nacos:
            server-addr: 10.168.1.99:8848
            dataId: ${spring.application.name}-flow-rules  # 都是在 sentinel源码持久化改造中的规则名称
            groupId: SENTINEL_GROUP 
            namespace: dev
            rule-type: flow
        degrade:
          nacos:
            server-addr: 10.168.1.99:8848
            dataId: ${spring.application.name}-degrade-rules # 都是在 sentinel源码持久化改造中的规则名称
            groupId: SENTINEL_GROUP
            namespace: dev
            rule-type: degrade
        system:
          nacos:
            server-addr: 10.168.1.99:8848
            dataId: ${spring.application.name}-system-rules # 都是在 sentinel源码持久化改造中的规则名称
            groupId: SENTINEL_GROUP
            namespace: dev
            rule-type: system
        authority:
          nacos:
            server-addr: 10.168.1.99:8848
            dataId: ${spring.application.name}-authority-rules # 都是在 sentinel源码持久化改造中的规则名称
            groupId: SENTINEL_GROUP
            namespace: dev
            rule-type: authority
        param-flow:
          nacos:
            server-addr: 10.168.1.99:8848
            dataId: ${spring.application.name}-param-flow-rules # 都是在 sentinel源码持久化改造中的规则名称
            groupId: SENTINEL_GROUP
            namespace: dev
            rule-type: param-flow

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐