sentinel结合nacos实现规则配置持久化

sentinel支持以下相关功能,但是功能相应的配置sentinel本身是基于内存存储的,如果应用服务重启,则规则配置信息就会丢失,这种情况在使用方面非常的不方便,所以就要做规则配置的持久化操作;本片文章就是基于sentinel+nacos进行规则配置持久化操作,具体功能如下

  1. 流量控制
  2. 熔断降级
  3. 系统自适应保护
  4. 集群流量控制
  5. 网关流量控制
  6. 热点参数限流
  7. 来源访问控制

由于sentinel的使用,大部分都是从官网下载sentinel的jar包,然后启动即可访问控制台,但是添加规则配置持久化操作,则需要调整sentinel-dashboard部分源码,调整使用的持久化插件。sentinel本身支持Apollo、Nacos、Zookeeper等插件进行规则配置持久化工作。
基于Apollo的文章地址:Sentinel结合Apollo实现规则持久化功能

1. 下载sentinel的源码,调整sentinel-dashboard部分源码

sentinel源码下载地址:https://github.com/alibaba 然后搜sentinel,即可看到哦;到Tags里面下载对应的版本即可。本文使用的是1.8.3版本

image.png

image.png

调整sentinel-dashboard部分代码:
将test包下面的nacos文件夹复制粘贴到src-main-java下的rule目录下,操作如下图:

image.png

工程最外层pom中的sentinel-datasource-nacos依赖下面的scope注释或删除掉

image.png

网关流控需要新建相应的类,包含添加GatewayFlowRuleNacosProvider,GatewayFlowRuleNacosPublisher两个配置类和修改GatewayFlowRuleController控制类,代码如下:
GatewayFlowRuleNacosProvider.java

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
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;

@Component("gatewayFlowRuleNacosProvider")
public class GatewayFlowRuleNacosProvider implements DynamicRuleProvider> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter> converter;

    @Override
    public List getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

GatewayFlowRuleNacosPublisher.java

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
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;

@Component("gatewayFlowRuleNacosPublisher")
public class GatewayFlowRuleNacosPublisher implements DynamicRulePublisher> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter, String> converter;

    @Override
    public void publish(String app, List rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, converter.convert(rules));
    }
}

GatewayFlowRuleController.java 该类全部完整代码如下: 具体修改位置如下图“”

import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayParamFlowItemEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.AddFlowRuleReqVo;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.GatewayParamFlowItemVo;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.UpdateFlowRuleReqVo;
import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemGatewayFlowRuleStore;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
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.Arrays;
import java.util.Date;
import java.util.List;

import static com.alibaba.csp.sentinel.slots.block.RuleConstant.*;
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*;
import static com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity.*;

/**
 * Gateway flow rule Controller for manage gateway flow rules.
 *
 * @author cdfive
 * @since 1.7.0
 */
@RestController
@RequestMapping(value = "/gateway/flow")
public class GatewayFlowRuleController {

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

    @Autowired
    private InMemGatewayFlowRuleStore repository;

//    @Autowired
//    private SentinelApiClient sentinelApiClient;

    @Autowired
    @Qualifier("gatewayFlowRuleNacosProvider")
    private DynamicRuleProvider> ruleProvider;
    @Autowired
    @Qualifier("gatewayFlowRuleNacosPublisher")
    private DynamicRulePublisher> rulePublisher;

    @GetMapping("/list.json")
    @AuthAction(AuthService.PrivilegeType.READ_RULE)
    public Result> queryFlowRules(String app, String ip, 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 rules = sentinelApiClient.fetchGatewayFlowRules(app, ip, port).get();
            List rules = ruleProvider.getRules(app);
            repository.saveAll(rules);
            return Result.ofSuccess(rules);
        } catch (Throwable throwable) {
            logger.error("query gateway flow rules error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }
    }

    @PostMapping("/new.json")
    @AuthAction(AuthService.PrivilegeType.WRITE_RULE)
    public Result addFlowRule(@RequestBody AddFlowRuleReqVo reqVo) {

        String app = reqVo.getApp();
        if (StringUtil.isBlank(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }

        GatewayFlowRuleEntity entity = new GatewayFlowRuleEntity();
        entity.setApp(app.trim());

        String ip = reqVo.getIp();
        if (StringUtil.isBlank(ip)) {
            return Result.ofFail(-1, "ip can't be null or empty");
        }
        entity.setIp(ip.trim());

        Integer port = reqVo.getPort();
        if (port == null) {
            return Result.ofFail(-1, "port can't be null");
        }
        entity.setPort(port);

        // API类型, Route ID或API分组
        Integer resourceMode = reqVo.getResourceMode();
        if (resourceMode == null) {
            return Result.ofFail(-1, "resourceMode can't be null");
        }
        if (!Arrays.asList(RESOURCE_MODE_ROUTE_ID, RESOURCE_MODE_CUSTOM_API_NAME).contains(resourceMode)) {
            return Result.ofFail(-1, "invalid resourceMode: " + resourceMode);
        }
        entity.setResourceMode(resourceMode);

        // API名称
        String resource = reqVo.getResource();
        if (StringUtil.isBlank(resource)) {
            return Result.ofFail(-1, "resource can't be null or empty");
        }
        entity.setResource(resource.trim());

        // 针对请求属性
        GatewayParamFlowItemVo paramItem = reqVo.getParamItem();
        if (paramItem != null) {
            GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity();
            entity.setParamItem(itemEntity);

            // 参数属性 0-ClientIP 1-Remote Host 2-Header 3-URL参数 4-Cookie
            Integer parseStrategy = paramItem.getParseStrategy();
            if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER
                    , PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
                return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy);
            }
            itemEntity.setParseStrategy(paramItem.getParseStrategy());

            // 当参数属性为2-Header 3-URL参数 4-Cookie时,参数名称必填
            if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
                // 参数名称
                String fieldName = paramItem.getFieldName();
                if (StringUtil.isBlank(fieldName)) {
                    return Result.ofFail(-1, "fieldName can't be null or empty");
                }
                itemEntity.setFieldName(paramItem.getFieldName());
            }

            String pattern = paramItem.getPattern();
            // 如果匹配串不为空,验证匹配模式
            if (StringUtil.isNotEmpty(pattern)) {
                itemEntity.setPattern(pattern);
                Integer matchStrategy = paramItem.getMatchStrategy();
                if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) {
                    return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy);
                }
                itemEntity.setMatchStrategy(matchStrategy);
            }
        }

        // 阈值类型 0-线程数 1-QPS
        Integer grade = reqVo.getGrade();
        if (grade == null) {
            return Result.ofFail(-1, "grade can't be null");
        }
        if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) {
            return Result.ofFail(-1, "invalid grade: " + grade);
        }
        entity.setGrade(grade);

        // QPS阈值
        Double count = reqVo.getCount();
        if (count == null) {
            return Result.ofFail(-1, "count can't be null");
        }
        if (count < 0) {
            return Result.ofFail(-1, "count should be at lease zero");
        }
        entity.setCount(count);

        // 间隔
        Long interval = reqVo.getInterval();
        if (interval == null) {
            return Result.ofFail(-1, "interval can't be null");
        }
        if (interval <= 0) {
            return Result.ofFail(-1, "interval should be greater than zero");
        }
        entity.setInterval(interval);

        // 间隔单位
        Integer intervalUnit = reqVo.getIntervalUnit();
        if (intervalUnit == null) {
            return Result.ofFail(-1, "intervalUnit can't be null");
        }
        if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) {
            return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit);
        }
        entity.setIntervalUnit(intervalUnit);

        // 流控方式 0-快速失败 2-匀速排队
        Integer controlBehavior = reqVo.getControlBehavior();
        if (controlBehavior == null) {
            return Result.ofFail(-1, "controlBehavior can't be null");
        }
        if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) {
            return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior);
        }
        entity.setControlBehavior(controlBehavior);

        if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) {
            // 0-快速失败, 则Burst size必填
            Integer burst = reqVo.getBurst();
            if (burst == null) {
                return Result.ofFail(-1, "burst can't be null");
            }
            if (burst < 0) {
                return Result.ofFail(-1, "invalid burst: " + burst);
            }
            entity.setBurst(burst);
        } else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) {
            // 1-匀速排队, 则超时时间必填
            Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs();
            if (maxQueueingTimeoutMs == null) {
                return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null");
            }
            if (maxQueueingTimeoutMs < 0) {
                return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs);
            }
            entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs);
        }

        Date date = new Date();
        entity.setGmtCreate(date);
        entity.setGmtModified(date);

        try {
            entity = repository.save(entity);
        } catch (Throwable throwable) {
            logger.error("add gateway flow rule error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }

//        if (!publishRules(app, ip, port)) {
//            logger.warn("publish gateway flow rules fail after add");
//        }

        try {
            publishRules(app, ip, port);
        } catch (Exception e) {
            logger.warn("publish gateway flow rules fail after add",e);
        }

        return Result.ofSuccess(entity);
    }

    @PostMapping("/save.json")
    @AuthAction(AuthService.PrivilegeType.WRITE_RULE)
    public Result updateFlowRule(@RequestBody UpdateFlowRuleReqVo reqVo) {

        String app = reqVo.getApp();
        if (StringUtil.isBlank(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }

        Long id = reqVo.getId();
        if (id == null) {
            return Result.ofFail(-1, "id can't be null");
        }

        GatewayFlowRuleEntity entity = repository.findById(id);
        if (entity == null) {
            return Result.ofFail(-1, "gateway flow rule does not exist, id=" + id);
        }

        // 针对请求属性
        GatewayParamFlowItemVo paramItem = reqVo.getParamItem();
        if (paramItem != null) {
            GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity();
            entity.setParamItem(itemEntity);

            // 参数属性 0-ClientIP 1-Remote Host 2-Header 3-URL参数 4-Cookie
            Integer parseStrategy = paramItem.getParseStrategy();
            if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER
                    , PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
                return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy);
            }
            itemEntity.setParseStrategy(paramItem.getParseStrategy());

            // 当参数属性为2-Header 3-URL参数 4-Cookie时,参数名称必填
            if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
                // 参数名称
                String fieldName = paramItem.getFieldName();
                if (StringUtil.isBlank(fieldName)) {
                    return Result.ofFail(-1, "fieldName can't be null or empty");
                }
                itemEntity.setFieldName(paramItem.getFieldName());
            }

            String pattern = paramItem.getPattern();
            // 如果匹配串不为空,验证匹配模式
            if (StringUtil.isNotEmpty(pattern)) {
                itemEntity.setPattern(pattern);
                Integer matchStrategy = paramItem.getMatchStrategy();
                if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) {
                    return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy);
                }
                itemEntity.setMatchStrategy(matchStrategy);
            }
        } else {
            entity.setParamItem(null);
        }

        // 阈值类型 0-线程数 1-QPS
        Integer grade = reqVo.getGrade();
        if (grade == null) {
            return Result.ofFail(-1, "grade can't be null");
        }
        if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) {
            return Result.ofFail(-1, "invalid grade: " + grade);
        }
        entity.setGrade(grade);

        // QPS阈值
        Double count = reqVo.getCount();
        if (count == null) {
            return Result.ofFail(-1, "count can't be null");
        }
        if (count < 0) {
            return Result.ofFail(-1, "count should be at lease zero");
        }
        entity.setCount(count);

        // 间隔
        Long interval = reqVo.getInterval();
        if (interval == null) {
            return Result.ofFail(-1, "interval can't be null");
        }
        if (interval <= 0) {
            return Result.ofFail(-1, "interval should be greater than zero");
        }
        entity.setInterval(interval);

        // 间隔单位
        Integer intervalUnit = reqVo.getIntervalUnit();
        if (intervalUnit == null) {
            return Result.ofFail(-1, "intervalUnit can't be null");
        }
        if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) {
            return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit);
        }
        entity.setIntervalUnit(intervalUnit);

        // 流控方式 0-快速失败 2-匀速排队
        Integer controlBehavior = reqVo.getControlBehavior();
        if (controlBehavior == null) {
            return Result.ofFail(-1, "controlBehavior can't be null");
        }
        if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) {
            return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior);
        }
        entity.setControlBehavior(controlBehavior);

        if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) {
            // 0-快速失败, 则Burst size必填
            Integer burst = reqVo.getBurst();
            if (burst == null) {
                return Result.ofFail(-1, "burst can't be null");
            }
            if (burst < 0) {
                return Result.ofFail(-1, "invalid burst: " + burst);
            }
            entity.setBurst(burst);
        } else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) {
            // 2-匀速排队, 则超时时间必填
            Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs();
            if (maxQueueingTimeoutMs == null) {
                return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null");
            }
            if (maxQueueingTimeoutMs < 0) {
                return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs);
            }
            entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs);
        }

        Date date = new Date();
        entity.setGmtModified(date);

        try {
            entity = repository.save(entity);
        } catch (Throwable throwable) {
            logger.error("update gateway flow rule error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }

//        if (!publishRules(app, entity.getIp(), entity.getPort())) {
//            logger.warn("publish gateway flow rules fail after update");
//        }
        try {
            publishRules(app, entity.getIp(), entity.getPort());
        } catch (Exception e) {
            logger.warn("publish gateway flow rules fail after update",e);
        }

        return Result.ofSuccess(entity);
    }


    @PostMapping("/delete.json")
    @AuthAction(AuthService.PrivilegeType.DELETE_RULE)
    public Result deleteFlowRule(Long id) {

        if (id == null) {
            return Result.ofFail(-1, "id can't be null");
        }

        GatewayFlowRuleEntity oldEntity = repository.findById(id);
        if (oldEntity == null) {
            return Result.ofSuccess(null);
        }

        try {
            repository.delete(id);
        } catch (Throwable throwable) {
            logger.error("delete gateway flow rule error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }

//        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
//            logger.warn("publish gateway flow rules fail after delete");
//        }

        try {
            publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort());
        } catch (Exception e) {
            logger.warn("publish gateway flow rules fail after delete",e);
        }

        return Result.ofSuccess(id);
    }

    private void publishRules(String app, String ip, Integer port) throws Exception{
        List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
        rulePublisher.publish(app, rules);
//        return sentinelApiClient.modifyGatewayFlowRules(app, ip, port, rules);
    }
}


调整NacosConfig.java配置类

image.png
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
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.beans.factory.annotation.Value;
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 {

    @Value("${naocs.address}")
    private String address;
    @Value("${nacos.namespace}")
    private String namespace;
    @Value("${nacos.clusterName}")
    private String clusterName;


    @Bean
    public Converter, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

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

    @Bean
    public ConfigService nacosConfigService() throws Exception {
        Properties properties = new Properties(); //定义nacos的配置属性
        properties.put(PropertyKeyConst.SERVER_ADDR,this.address);
        properties.put(PropertyKeyConst.NAMESPACE,this.namespace);
        properties.put(PropertyKeyConst.CLUSTER_NAME,this.clusterName);
        return ConfigFactory.createConfigService(properties);
//        return ConfigFactory.createConfigService("127.0.0.1:8082");
    }

    @Bean
    public Converter, String> gatewayFlowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter> gatewayFlowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);
    }
}

调整FlowControllerV2.java类加载配置

NacosConfigUtil.java添加常量值如下:

public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-flow-rules";

application.properties配置文件

naocs.address=127.0.0.1:8082
nacos.namespace=Dev
nacos.clusterName=

# 如果SpringBoot版本自行升级到2.5.X以上版本,则需要加下面这一行配置,解决版本升级后的双斜杠导致的404问题
spring.mvc.pathmatch.matching-strategy = ant_path_matcher

通过以上调整,sentinel-dashboard就调整完了,打包进行部署启动即可!!!

2. 应用服务添加配置及nacos配置中心添加的配置

pom依赖配置

        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-sentinel
        
        
            com.alibaba.csp
            sentinel-datasource-nacos
        

yml文件配置

spring:
  application.name: SentinelService
  cloud:
    sentinel:
      log:
        dir: /logs
        switch-pid: true
      transport:
        dashboard: 127.0.0.1:8080
      datasource:
        flow: #流控规则
          nacos:
            namespace: Dev
            server-addr: 127.0.0.1:8082
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: flow

测试接口如下:

@RestController
public class TestController {

    @GetMapping(value = "/get")
    public DataResult getMethod(){
        DataResult dataResult = new DataResult<>();
        dataResult.setCode(HttpStatus.OK.value());
        dataResult.setData("Success");
        return dataResult;
    }
}

nacos配置
在nacos上面需要创建命名空间,新增配置等操作;
配置命名空间是我之前创建好的,我主要用来区分环境,所以我这里就直接使用了Dev命名空间,如上述配置
新增配置SentinelService-flow-rules,配置DataId的规则是"服务名-flow-rules",配置的内容如下:

[{
    "resource": "/get",
    "limitApp": "default",
    "grade": 1,
    "count": 2,
    "clusterMode": false,
    "controlBehavior": 0,
    "strategy": 0,
    "warmUpPeriodSec": 10,
    "maxQueueingTimeMs": 500,
    "refResource": "rrr"
}]

通过上述配置,就完成了sentinel+nacos流量控制规则的持久化工作,启动应用服务,多次访问/get接口,大约10秒左右,就可以看到sentinel控制台上的数据了,其中流量控制页面就会出现nacos中的配置数据。结果参考下图:




以上只提供了流控规则的相关json及yml配置数据,下面提供一下其他规则的json配置及yml配置,以及相关的参数说明
json参数是配置nacos使用的,但是提供的json串中包含注释,使用时需要将注释删掉,保证json串的正确性

流控规则

[
  {
    // 资源名
    "resource": "/test",
    // 针对来源,若为 default 则不区分调用来源
    "limitApp": "default",
    // 限流阈值类型(1:QPS;0:并发线程数)
    "grade": 1,
    // 阈值
    "count": 1,
    // 是否是集群模式
    "clusterMode": false,
    // 流控效果(0:快速失败;1:Warm Up(预热模式);2:排队等待)
    "controlBehavior": 0,
    // 流控模式(0:直接;1:关联;2:链路)
    "strategy": 0,
    // 预热时间(秒,预热模式需要此参数)
    "warmUpPeriodSec": 10,
    // 超时时间(排队等待模式需要此参数)
    "maxQueueingTimeMs": 500,
    // 关联资源、入口资源(关联、链路模式)
    "refResource": "rrr"
  }
]

熔断规则

[
  {
      // 资源名
    "resource": "/test1",
    "limitApp": "default",
    // 熔断策略(0:慢调用比例,1:异常比率,2:异常计数)
    "grade": 0,
    // 最大RT、比例阈值、异常数
    "count": 200,
    // 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
    "slowRatioThreshold": 0.2,
    // 最小请求数
    "minRequestAmount": 5,
    // 当单位统计时长(类中默认1000)
    "statIntervalMs": 1000,
    // 熔断时长
    "timeWindow": 10
  }
]

热点规则

[
  {
    // 资源名
    "resource": "/test1",
    // 限流模式(QPS 模式,不可更改)
    "grade": 1,
    // 参数索引
    "paramIdx": 0,
    // 单机阈值
    "count": 13,
    // 统计窗口时长
    "durationInSec": 6,
    // 是否集群 默认false
    "clusterMode": false,
    //
    "burstCount": 0,
    // 集群模式配置
    "clusterConfig": {
      //
      "fallbackToLocalWhenFail": true,
      //
      "flowId": 2,
      //
      "sampleCount": 10,
      //
      "thresholdType": 0,
      //
      "windowIntervalMs": 1000
    },
    // 流控效果(支持快速失败和匀速排队模式)
    "controlBehavior": 0,
    //
    "limitApp": "default",
    //
    "maxQueueingTimeMs": 0,
    // 高级选项
    "paramFlowItemList": [
      {
        // 参数类型
        "classType": "int",
        // 限流阈值
        "count": 222,
        // 参数值
        "object": "2"
      }
    ]
  }
]

系统规则

  [
    {
      // RT
      "avgRt": 1,
      // CPU 使用率
      "highestCpuUsage": -1,
      // LOAD
      "highestSystemLoad": -1,
      // 线程数
      "maxThread": -1,
      // 入口 QPS
      "qps": -1
    }
  ]

授权规则

[
  {
    // 资源名
    "resource": "sentinel_spring_web_context",
    // 流控应用
    "limitApp": "/test",
    // 授权类型(0代表白名单;1代表黑名单。)
    "strategy": 0
  }
]

各类规则对应的yml配置

spring:
  application.name: SentinelService
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8082  #Nacos 服务地址
    sentinel:
      log:
        dir: /logs
        switch-pid: true
      transport:
        dashboard: 127.0.0.1:8080
      datasource:
        flow: #流控规则
          nacos:
            namespace: Dev
            server-addr: 127.0.0.1:8082
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: flow
        degrade: #熔断规则
          nacos:
            namespace: Dev
            server-addr: 127.0.0.1:8082
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: degrade
        system: #系统规则
          nacos:
            namespace: Dev
            server-addr: 127.0.0.1:8082
            dataId: ${spring.application.name}-system-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: system
        authority: #授权规则
          nacos:
            namespace: Dev
            server-addr: 127.0.0.1:8082
            dataId: ${spring.application.name}-authority-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: authority
        param-flow: #热点参数规则
          nacos:
            namespace: Dev
            server-addr: 127.0.0.1:8082
            dataId: ${spring.application.name}-param-flow-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: param-flow

以上提供了五种规则的nacos配置及yml配置,可以完整的支持sentinel规则配置持久化功能!

如有问题欢迎评论留言沟通!

你可能感兴趣的:(sentinel结合nacos实现规则配置持久化)