springcloud+dubbo+sentinel+nacos搭建(sentinel规则管理及推送、持久化)

前面两篇讲了不用springcloud做粘合,直接springboot+dubbo+nacos+sentinel的搭建

这篇讲用springcloud做粘合是怎么搞的

一、springcloud+sentinel

在前面的项目的基础上加入

        <!--sentinel 核心环境 依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>

application.yml加入配置

    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8080

代码中用@SentinelResource标记要限流的资源

@RestController
public class AccountController {

    @Autowired
    AccountService accountService;

    @SentinelResource(value = "accountController-get")
    @RequestMapping(value = "account/get", method = RequestMethod.GET)
    public String get(String userId) throws InterruptedException {
        Thread.sleep(1000);
        return "done";
    }

}

启动后调用一下这个controller这样才能在sentinel界面看到

添加一个流控规则试一下

频繁多点几次就会出现限流

二、springcloud+dubbo+sentinel

前面讲的相当于是直接用springcloud+sentinel,在http接口的地方用@SentinelResource标记资源。

下面讲在dubbo的RPC调用的时候怎么结合springcloud+sentinel用

引入依赖

        <!--sentinel 核心环境 依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
      
        <!-- sentinel与dubbo适配需要的适配   对于 Apache Dubbo 2.7.x 及以上版本-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-apache-dubbo-adapter</artifactId>
            <version>1.7.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.7.2</version>
        </dependency>

注意要加入sentinel-transport-simlpe-http,不然启动要报错

Exception in thread "Thread-16" java.lang.NoClassDefFoundError: com/alibaba/csp/sentinel/log/CommandCenterLog

启动起来,然后出发一下dubbo的RPC调用。就能在sentinel的控制界面的簇点链路中看到了

三、sentinel配置的规则做持久化

目前这样,sentinel上面配置的流控规则这些,一旦把sentinel重启就会消失。

一般来说,规则的推送有下面三种模式

推送模式说明优点缺点
原始模式API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource简单,无任何依赖不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境
Pull 模式扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等简单,无任何依赖;规则持久化不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。
Push 模式扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。规则持久化;一致性;快速引入第三方依赖

1.原始模式

如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中:

Original push rules from Sentinel Dashboard

这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。

2.Pull模式

pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。

使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。

原理:

本地文件数据源会定时轮询文件的变更,读取规则。

这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。

以本地文件数据源为例,推送过程如下图所示:

Push rules from Sentinel Dashboard to local file

首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。

使用 pull 模式的数据源时一般不需要对 Sentinel 控制台进行改造。

这种实现方法好处是简单,不引入新的依赖,坏处是无法保证监控数据的一致性。

Pull 优缺点

优点

简单,无任何依赖

没有额外依赖

缺点

不保证一致性(规则是使用FileRefreshableDataSource定时更新,会有延迟)

实时性不保证(规则是使用FileRefreshableDataSource定时更新)

拉取过于频繁也可能会有性能问题

由于文件存储于本地,容易丢失

以本地文件数据源为例

在上面已有了的依赖的情况下加入依赖

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-parameter-flow-control</artifactId>
            <version>1.7.2</version>
        </dependency>

常量类定义规则文件的目录和名字

package com.sid.config;

import java.util.HashMap;
import java.util.Map;

/**
 * 常量类,主要是定义规则文件的目录和名字
 * */
public class PersistenceRuleConstant {
    /**
     * 存储文件路径
     */
    public static final String storePath = System.getProperty("user.home") + "\\sentinel\\rules\\order-app\\";

    /**
     * 各种存储sentinel规则映射map
     */
    public static final Map<String, String> rulesMap = new HashMap<String, String>();

    //流控规则文件
    public static final String FLOW_RULE_PATH = "flowRulePath";

    //降级规则文件
    public static final String DEGRAGE_RULE_PATH = "degradeRulePath";

    //授权规则文件
    public static final String AUTH_RULE_PATH = "authRulePath";

    //系统规则文件
    public static final String SYSTEM_RULE_PATH = "systemRulePath";

    //热点参数文件
    public static final String HOT_PARAM_RULE = "hotParamRulePath";

    static {
        rulesMap.put(FLOW_RULE_PATH, storePath + "flowRule.json");
        rulesMap.put(DEGRAGE_RULE_PATH, storePath + "degradeRule.json");
        rulesMap.put(SYSTEM_RULE_PATH, storePath + "systemRule.json");
        rulesMap.put(AUTH_RULE_PATH, storePath + "authRule.json");
        rulesMap.put(HOT_PARAM_RULE, storePath + "hotParamRule.json");
    }
}

文件操作类:如果规则文件不存在就创建对应的目录和对应的规则文件

package com.sid.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 文件操作类,如果规则文件不存在就创建对应的目录和对应的规则文件
 * */
public class RuleFileUtils {
    private static final Logger logger = LoggerFactory.getLogger(RuleFileUtils.class);

    /**
     * 方法实现说明:若路径不存在就创建路径
     *
     * @param filePath:文件存储路径
     */
    public static void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            logger.info("创建Sentinel规则目录:{}", filePath);
            file.mkdirs();
        }
    }


    /**
     * 方法实现说明:若文件不存在就创建路径
     *
     * @param ruleFileMap 规则存储文件
     */
    public static void createFileIfNotExits(Map<String, String> ruleFileMap) throws IOException {

        Set<String> ruleFilePathSet = ruleFileMap.keySet();

        Iterator<String> ruleFilePathIter = ruleFilePathSet.iterator();

        while (ruleFilePathIter.hasNext()) {
            String ruleFilePathKey = ruleFilePathIter.next();
            String ruleFilePath = PersistenceRuleConstant.rulesMap.get(ruleFilePathKey).toString();
            File ruleFile = new File(ruleFilePath);
            if (ruleFile.exists()) {
                logger.info("创建Sentinel 规则文件:{}", ruleFile);
                ruleFile.createNewFile();
            }
        }
    }
}

 规则的编码和解码操作类

package com.sid.config;

import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;

import java.util.List;

/**
 * 规则的编码和解码操作类
 * */
public class RuleListParserUtils {
    /**
     * 流控列表解析器
     */
    public static final Converter<String, List<FlowRule>> flowRuleListParser = new Converter<String, List<FlowRule>>() {
        @Override
        public List<FlowRule> convert(String source) {
            return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
            });
        }
    };

    /**
     * 流控列表 编码器
     */
    public static final Converter<List<FlowRule>, String> flowRuleEnCoding = new Converter<List<FlowRule>, String>() {
        @Override
        public String convert(List<FlowRule> source) {
            return JSON.toJSONString(source);
        }
    };

    public static final Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(source,
            new TypeReference<List<DegradeRule>>() {
            });

    public static final Converter<List<DegradeRule>, String> degradeRuleEnCoding = new Converter<List<DegradeRule>, String>() {
        @Override
        public String convert(List<DegradeRule> source) {
            return JSON.toJSONString(source);
        }
    };

    public static final Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(source,
            new TypeReference<List<SystemRule>>() {
            });

    public static final Converter<List<SystemRule>, String> systemRuleEnCoding = new Converter<List<SystemRule>, String>() {
        @Override
        public String convert(List<SystemRule> source) {
            return JSON.toJSONString(source);
        }
    };

    public static final Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON
            .parseObject(source, new TypeReference<List<AuthorityRule>>() {
            });


    public static final Converter<List<AuthorityRule>, String> authorityRuleEnCoding = new Converter<List<AuthorityRule>, String>() {
        @Override
        public String convert(List<AuthorityRule> source) {
            return JSON.toJSONString(source);
        }
    };

    public static final Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON
            .parseObject(source, new TypeReference<List<ParamFlowRule>>() {
            });


//    public static final Converter<List<ParamFlowRule>, String> paramFlowRuleEnCoding = new Converter<List<ParamFlowRule>, String>() {
//        @Override
//        public String convert(List<ParamFlowRule> source) {
//            return JSON.toJSONString(source);
//        }
//    };

    public static final Converter<List<ParamFlowRule>, String> paramFlowRuleEnCoding = source -> encodeJson(source);

    private static <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }


}

pull模式操作类

package com.sid.config;

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
import com.alibaba.csp.sentinel.datasource.FileWritableDataSource;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileNotFoundException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。
 * <p>
 * 使用时需要在客户端注册数据源:
 * 将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。
 */
public class PullModeLocalFileDataSource implements InitFunc {

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

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    @Override
    public void init() throws Exception {
        logger.info("time:{}读取配置", sdf.format(new Date()));
        try {
            // 创建文件存储目录(若路径不存在就创建路径)
            RuleFileUtils.mkdirIfNotExits(PersistenceRuleConstant.storePath);
            // 创建规则文件()
            RuleFileUtils.createFileIfNotExits(PersistenceRuleConstant.rulesMap);
            // 处理流控规则逻辑
            dealFlowRules();
            // 处理降级规则
            dealDegradeRules();
            // 处理系统规则
            dealSystemRules();
            // 热点参数规则
            dealParamFlowRules();
            //  授权规则
            dealAuthRules();
        } catch (Exception e) {
            logger.error("错误原因:{}", e);
        }

    }

    /**
     * 方法实现说明:处理流控规则逻辑
     */
    private void dealFlowRules() throws FileNotFoundException {
        String ruleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.FLOW_RULE_PATH).toString();
        // 创建流控规则的可读数据源
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(ruleFilePath,
                RuleListParserUtils.flowRuleListParser);
        // 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<List<FlowRule>>(ruleFilePath,
                RuleListParserUtils.flowRuleEnCoding);

        // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
        //  这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
    }

    // 处理降级规则
    private void dealDegradeRules() throws FileNotFoundException {
        String degradeRulePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.DEGRAGE_RULE_PATH).toString();
        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(degradeRulePath,
                RuleListParserUtils.degradeRuleListParser);
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(degradeRulePath,
                RuleListParserUtils.degradeRuleEnCoding);
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

    }

    // 处理系统规则
    private void dealSystemRules() throws FileNotFoundException {
        String systemRulePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.SYSTEM_RULE_PATH).toString();
        // 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(systemRulePath,
                RuleListParserUtils.systemRuleListParser);
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(systemRulePath,
                RuleListParserUtils.systemRuleEnCoding);
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
    }

    // 热点参数规则
    private void dealParamFlowRules() throws FileNotFoundException {
        String paramFlowRulePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.HOT_PARAM_RULE).toString();
        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath, RuleListParserUtils.paramFlowRuleListParser);
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(paramFlowRulePath,
                RuleListParserUtils.paramFlowRuleEnCoding);
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    private void dealAuthRules() throws FileNotFoundException {
        String authFlowRulePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.AUTH_RULE_PATH).toString();
//授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(authFlowRulePath,
                RuleListParserUtils.authorityRuleListParser);
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(authFlowRulePath,
                RuleListParserUtils.authorityRuleEnCoding);
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
    }

}

 通过SPI扩展机制进行扩展,在微服务工程的resources目录下创建META-INF/services目录,并新建文件名为com.alibaba.csp.sentinel.init.InitFunc文件。内容是PullModeLocalFileDataSource类全路径类名

验证

在sentinel dashboard(http://localhost:8080/)中给app-order项目添加流控规则

app-order检测到流控规则的变化并产生flowRule.json文件 

[{"clusterConfig":{"fallbackToLocalWhenFail":true,"sampleCount":10,"strategy":0,"thresholdType":0,"windowIntervalMs":1000},"clusterMode":false,"controlBehavior":0,"count":1.0,"grade":1,"limitApp":"default","maxQueueingTimeMs":500,"resource":"com.sid.rpc.service.service.OrderServiceApi:create(java.lang.String,java.lang.String,java.lang.Integer)","strategy":0,"warmUpPeriodSec":10}]

直接修改这个flowRule.json文件中的内容,也会在 sentinel dashboard(http://localhost:8080/)中看到变化

重启项目或者重启sentinel dashboard,能看到之前配置的流控信息都在

3.Push模式(持久化到nacos)

生产环境下一般更常用的是 push 模式的数据源。

对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。

Remote push rules to config center

换句话说就是实现Sentinel Dashboard与Nacos之间的相互通信:

1.Sentinel Dashboard界面配置流控规则---sentinel发布/推送--->Nacos生成配置文件并持久化;

2.通过Nacos配置文件修改流控规则---sentinel拉取--->Sentinel Dashboard界面显示最新的流控规则。

需要注意的是:

在Nacos控制台上修改流控制,虽然可以同步到Sentinel Dashboard,但是Nacos此时应该作为一个流控规则的持久化平台,所以正常操作过程应该是开发者在Sentinel Dashboard上修改流控规则后同步到Nacos,遗憾的是目前Sentinel Dashboard不支持该功能。

试想下,如果公司没有统一在Sentinel Dashboard或Nacos中二选一进行配置,而是一会在Sentinel Dashboard配置,一会在Nacos配置。那么就会出现很严重的问题(流控规则达不到预期,配置数据不一致),所以推荐使用Sentinel Dashboard统一界面进行配置管理流控规则

正因为Sentinel Dashboard当前版本(截至目前为止是1.8.1-SNAPSHOT)暂不支持,但是可以通过改造部分源码实现此功能

修改sentinel-dashboard项目源码

先下载sentinel源码:https://github.com/alibaba/Sentinel

切换到自己对应的分支(1.8.1)

控制台改造主要是为规则实现

  • DynamicRuleProvider:从Nacos上读取配置
  • DynamicRulePublisher:将规则推送到Nacos上

pom中

<!-- for Nacos rule publisher sample -->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
        <!--注释掉原文件中的scope,让其不仅在test的时候生效-->
        <!--<scope>test</scope>-->
    </dependency>

复制sentinel-dashboard项目下test下的nacos包

src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos到 src/main/java/com/alibaba/csp/sentinel/dashboard/rule下

 

修改controller中的默认provider & publisher

com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2中

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

打开 /Sentinel-1.8.1/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html文件,修改代码

          <li ui-sref-active="active" ng-if="!entry.isGateway">
            <a ui-sref="dashboard.flowV1({app: entry.app})">
              <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
          </li>


把V1去掉,就是这个flowV1改成flow

 

复制过来的这几个类要修改,且新增一个NacosPropertiesConfiguration类

package com.alibaba.csp.sentinel.dashboard.rule.nacos;


import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "sentinel.nacos")
public class NacosPropertiesConfiguration{
    private String serverAddr;
    private String dataId;
    private String groupId = "SENTINEL_GROUP"; // 默认分组
    private String namespace;

//省略getter setter
}

NacosConfig要修改为

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.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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
 */
@EnableConfigurationProperties(NacosPropertiesConfiguration.class)
@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 ConfigService nacosConfigService(NacosPropertiesConfiguration nacosPropertiesConfiguration) throws Exception {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfiguration.getServerAddr());
        properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfiguration.getNamespace());
        return ConfigFactory.createConfigService(properties);
//        return ConfigFactory.createConfigService("localhost");
    }
}

修改FlowRuleNacosPublisher


@Service("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @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 name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, converter.convert(rules));
    }
}

修改类FlowRuleNacosProvider


@Service("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);
    }
}

修改配置文件application.properties在末尾加入

# nacos config server
sentinel.nacos.serverAddr=localhost:8848
sentinel.nacos.namespace=
sentinel.nacos.group-id=SENTINEL_GROUP

 重新打包sentinel项目(记得是把整个sentinel项目重新打包,不是只打包dashboard,怕有东西没打进去)

mvn clean package -DskipTests

启动这个打出来的包

业务系统代码修改

引入依赖

       <!-- Sentinel支持采用 Nacos 作为规则配置数据源,引入该适配依赖-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
            <version>1.7.2</version>
        </dependency>

application.yml添加配置

      datasource:
        #自定义的数据源名称,可以多个,必须唯一,可以理解为通过该数据源指定
        #读取哪个位置的哪个文件中的数据,此处表示读取 Nacos 配置中心上的
        #名为 cloudalibaba-sentinel-service 类型为 json 文件中的数据
        #前提是指定的 Nacos 配置中心中要有该文件,该文件中保存的是,Sentinel
        #对当前服务或服务中的接口设置的流控,熔断降级等相关的数据
        ds1:
          nacos:
            server-addr: localhost:8848 #存放数据的数据源地址,通过 Nacos 保存相关数据
            dataId: app-account-flow-rules #读取 Ncaos 配置中心上文件的名称
            groupId: SENTINEL_GROUP #读取 Nacos 配置中心哪个分组id
            data-type: json #读取 Nacos 配置中心上的什么类型的文件
            rule-type: flow #读取的规则类型

在nacos中创建sentinel的规则配置文件

[
    {
        "resource": "account-debit",//对应 Sentinel 中的资源名称(接口请求路径,或指定的资源名称)
        "limitApp": "default",//对应 Sentinel 中的来源应用(在Stinel中一般使用默认的)
        "grade": 1, //对应 Sentinel 设置的阈值类型,0 表示线程数,1 表示QPS
        "count": 10,//对应 Sentinel 设置的单机阈值
        "strategy": 0,//对应 Sentinel 设置的流程模式,0直接,1关联,2链路
        "controlBehavior": 0,//对应 Sentinel 流程效果,0快速失败,1WarmUp,2排队等待
        "clusterMode": false //对应 Sentinel 设置的是否集群
    }
]

在项目启动后访问该服务中的任意一个接口时,会自动通过配置的 Sentinel 数据源读取 Nacos 上的指定的文件,加载配置内容,获取到针对该服务的流控,熔断,降级等相关数据

(如果启动服务不妨问服务中的任何借口,默认是不会主动去读取数据的,Sentinel 流控页显示为空)

可以尝试在sentinel改变阈值,在nacos中能看到。(失败、好吧,是我理解错了)

在nacos中改变阈值,在sentinel中能看到。(成功)

那在sentinel中改变了阈值,在nacos中看不到,问题出来哪里呢,经过尝试,发现本来我nacos没有用mysql,如果我把我nacos本身的数据存储到mysql中,那sentinel修改后会同步到nacos上

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 Spring Cloud Alibaba 技术栈搭建项目手脚架可以提高开发效率和项目可维护性,以下是基于 Spring Cloud Alibaba 技术栈的项目手脚架搭建步骤: 1. 环境准备 确保已安装 JDK,Maven,Nacos,RocketMQ,Seata 和 Sentinel 等必需的软件。 2. 创建 Spring Boot 项目 使用 Spring Initializr 创建一个 Spring Boot 项目,添加必需的依赖项: ```xml <!-- Spring Boot 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Cloud Alibaba 依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-dubbo</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency> <!-- RocketMQ 依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-stream-rocketmq</artifactId> </dependency> ``` 3. 配置 Nacos 在 application.properties 文件中添加 Nacos 相关配置: ```properties spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.namespace=dev ``` 4. 配置 Dubbo 在 application.properties 文件中添加 Dubbo 相关配置: ```properties spring.application.name=service-provider spring.cloud.dubbo.application.name=service-provider spring.cloud.dubbo.registry.address=nacos://127.0.0.1:8848 spring.cloud.dubbo.protocol.name=dubbo spring.cloud.dubbo.protocol.port=20880 ``` 5. 配置 RocketMQ 在 application.properties 文件中添加 RocketMQ 相关配置: ```properties spring.cloud.stream.rocketmq.binder.name-server=127.0.0.1:9876 spring.cloud.stream.rocketmq.binder.group=group1 ``` 6. 配置 Seata 在 application.properties 文件中添加 Seata 相关配置: ```properties spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group ``` 7. 配置 Sentinel 在 application.properties 文件中添加 Sentinel 相关配置: ```properties spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080 ``` 8. 创建 Dubbo 服务接口 ```java public interface HelloService { String sayHello(String name); } ``` 9. 实现 Dubbo 服务接口 ```java @Service(version = "1.0.0") public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello, " + name; } } ``` 10. 配置 Dubbo 服务提供者 ```java @Configuration public class DubboProviderConfig { @Bean public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("service-provider"); return applicationConfig; } @Bean public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("nacos://127.0.0.1:8848"); return registryConfig; } @Bean public ProtocolConfig protocolConfig() { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(20880); return protocolConfig; } @Bean public ProviderConfig providerConfig() { ProviderConfig providerConfig = new ProviderConfig(); providerConfig.setTimeout(3000); return providerConfig; } @Bean public ServiceConfig<HelloService> helloServiceServiceConfig() { ServiceConfig<HelloService> serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(HelloService.class); serviceConfig.setRef(new HelloServiceImpl()); serviceConfig.setVersion("1.0.0"); return serviceConfig; } } ``` 11. 创建测试类 ```java @RunWith(SpringRunner.class) @SpringBootTest public class HelloServiceTest { @Reference(version = "1.0.0") private HelloService helloService; @Test public void sayHelloTest() { String result = helloService.sayHello("World"); Assert.assertEquals("Hello, World", result); } } ``` 12. 启动项目 正常启动项目,并在浏览器中访问 http://localhost:8848/nacos,可以看到 Nacos 控制台。 在 Dubbo 服务提供者启动后,在 Dubbo 服务消费者中调用 Dubbo 服务,验证服务调用成功。 在 Sentinel 控制台中配置相关规则,验证 Sentinel 报警和限流功能。 在 RocketMQ 控制台中创建一个主题,发送消息,验证消息发送和接收功能。 在 Seata 控制台中创建一个事务分组,验证分布式事务功能。 以上就是使用 Spring Cloud Alibaba 技术栈搭建项目手脚架的步骤,可以根据实际需求进行扩展和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值