sentinel結(jié)合nacos實(shí)現(xiàn)規(guī)則配置持久化

sentinel支持以下相關(guān)功能邢锯,但是功能相應(yīng)的配置sentinel本身是基于內(nèi)存存儲(chǔ)的彼城,如果應(yīng)用服務(wù)重啟,則規(guī)則配置信息就會(huì)丟失候生,這種情況在使用方面非常的不方便,所以就要做規(guī)則配置的持久化操作绽昼;本片文章就是基于sentinel+nacos進(jìn)行規(guī)則配置持久化操作唯鸭,具體功能如下

  1. 流量控制
  2. 熔斷降級(jí)
  3. 系統(tǒng)自適應(yīng)保護(hù)
  4. 集群流量控制
  5. 網(wǎng)關(guān)流量控制
  6. 熱點(diǎn)參數(shù)限流
  7. 來(lái)源訪問(wèn)控制

由于sentinel的使用,大部分都是從官網(wǎng)下載sentinel的jar包硅确,然后啟動(dòng)即可訪問(wèn)控制臺(tái)目溉,但是添加規(guī)則配置持久化操作,則需要調(diào)整sentinel-dashboard部分源碼菱农,調(diào)整使用的持久化插件缭付。sentinel本身支持Apollo、Nacos循未、Zookeeper等插件進(jìn)行規(guī)則配置持久化工作陷猫。
基于Apollo的文章地址:Sentinel結(jié)合Apollo實(shí)現(xiàn)規(guī)則持久化功能

1. 下載sentinel的源碼,調(diào)整sentinel-dashboard部分源碼

sentinel源碼下載地址:https://github.com/alibaba 然后搜sentinel的妖,即可看到哦绣檬;到Tags里面下載對(duì)應(yīng)的版本即可。本文使用的是1.8.3版本

image.png

image.png

調(diào)整sentinel-dashboard部分代碼:
將test包下面的nacos文件夾復(fù)制粘貼到src-main-java下的rule目錄下嫂粟,操作如下圖:

image.png

工程最外層pom中的sentinel-datasource-nacos依賴下面的scope注釋或刪除掉

image.png

網(wǎng)關(guān)流控需要新建相應(yīng)的類娇未,包含添加GatewayFlowRuleNacosProvider,GatewayFlowRuleNacosPublisher兩個(gè)配置類和修改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<List<GatewayFlowRuleEntity>> {

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

    @Override
    public List<GatewayFlowRuleEntity> 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<List<GatewayFlowRuleEntity>> {

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

    @Override
    public void publish(String app, List<GatewayFlowRuleEntity> 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<List<GatewayFlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("gatewayFlowRuleNacosPublisher")
    private DynamicRulePublisher<List<GatewayFlowRuleEntity>> rulePublisher;

    @GetMapping("/list.json")
    @AuthAction(AuthService.PrivilegeType.READ_RULE)
    public Result<List<GatewayFlowRuleEntity>> 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<GatewayFlowRuleEntity> rules = sentinelApiClient.fetchGatewayFlowRules(app, ip, port).get();
            List<GatewayFlowRuleEntity> 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<GatewayFlowRuleEntity> 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());

        // 針對(duì)請(qǐng)求屬性
        GatewayParamFlowItemVo paramItem = reqVo.getParamItem();
        if (paramItem != null) {
            GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity();
            entity.setParamItem(itemEntity);

            // 參數(shù)屬性 0-ClientIP 1-Remote Host 2-Header 3-URL參數(shù) 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());

            // 當(dāng)參數(shù)屬性為2-Header 3-URL參數(shù) 4-Cookie時(shí)星虹,參數(shù)名稱必填
            if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
                // 參數(shù)名稱
                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();
            // 如果匹配串不為空忘蟹,驗(yàn)證匹配模式
            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-線程數(shù) 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-勻速排隊(duì)
        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-勻速排隊(duì), 則超時(shí)時(shí)間必填
            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<GatewayFlowRuleEntity> 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);
        }

        // 針對(duì)請(qǐng)求屬性
        GatewayParamFlowItemVo paramItem = reqVo.getParamItem();
        if (paramItem != null) {
            GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity();
            entity.setParamItem(itemEntity);

            // 參數(shù)屬性 0-ClientIP 1-Remote Host 2-Header 3-URL參數(shù) 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());

            // 當(dāng)參數(shù)屬性為2-Header 3-URL參數(shù) 4-Cookie時(shí)飒房,參數(shù)名稱必填
            if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
                // 參數(shù)名稱
                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();
            // 如果匹配串不為空,驗(yàn)證匹配模式
            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-線程數(shù) 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-勻速排隊(duì)
        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-勻速排隊(duì), 則超時(shí)時(shí)間必填
            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<Long> 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<GatewayFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
        rulePublisher.publish(app, rules);
//        return sentinelApiClient.modifyGatewayFlowRules(app, ip, port, rules);
    }
}


調(diào)整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<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() 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<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

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

調(diào)整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版本自行升級(jí)到2.5.X以上版本媚值,則需要加下面這一行配置狠毯,解決版本升級(jí)后的雙斜杠導(dǎo)致的404問(wèn)題
spring.mvc.pathmatch.matching-strategy = ant_path_matcher

通過(guò)以上調(diào)整,sentinel-dashboard就調(diào)整完了褥芒,打包進(jìn)行部署啟動(dòng)即可=浪伞!锰扶!

2. 應(yīng)用服務(wù)添加配置及nacos配置中心添加的配置

pom依賴配置

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

yml文件配置

spring:
  application.name: SentinelService
  cloud:
    sentinel:
      log:
        dir: /logs
        switch-pid: true
      transport:
        dashboard: 127.0.0.1:8080
      datasource:
        flow: #流控規(guī)則
          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

測(cè)試接口如下:

@RestController
public class TestController {

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

nacos配置
在nacos上面需要?jiǎng)?chuàng)建命名空間献酗,新增配置等操作;
配置命名空間是我之前創(chuàng)建好的坷牛,我主要用來(lái)區(qū)分環(huán)境罕偎,所以我這里就直接使用了Dev命名空間,如上述配置
新增配置SentinelService-flow-rules京闰,配置DataId的規(guī)則是"服務(wù)名-flow-rules"颜及,配置的內(nèi)容如下:

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

通過(guò)上述配置,就完成了sentinel+nacos流量控制規(guī)則的持久化工作蹂楣,啟動(dòng)應(yīng)用服務(wù)俏站,多次訪問(wèn)/get接口,大約10秒左右痊土,就可以看到sentinel控制臺(tái)上的數(shù)據(jù)了肄扎,其中流量控制頁(yè)面就會(huì)出現(xiàn)nacos中的配置數(shù)據(jù)。結(jié)果參考下圖:




以上只提供了流控規(guī)則的相關(guān)json及yml配置數(shù)據(jù)赁酝,下面提供一下其他規(guī)則的json配置及yml配置犯祠,以及相關(guān)的參數(shù)說(shuō)明
json參數(shù)是配置nacos使用的,但是提供的json串中包含注釋酌呆,使用時(shí)需要將注釋刪掉雷则,保證json串的正確性

流控規(guī)則

[
  {
    // 資源名
    "resource": "/test",
    // 針對(duì)來(lái)源,若為 default 則不區(qū)分調(diào)用來(lái)源
    "limitApp": "default",
    // 限流閾值類型(1:QPS;0:并發(fā)線程數(shù))
    "grade": 1,
    // 閾值
    "count": 1,
    // 是否是集群模式
    "clusterMode": false,
    // 流控效果(0:快速失敗;1:Warm Up(預(yù)熱模式);2:排隊(duì)等待)
    "controlBehavior": 0,
    // 流控模式(0:直接肪笋;1:關(guān)聯(lián);2:鏈路)
    "strategy": 0,
    // 預(yù)熱時(shí)間(秒月劈,預(yù)熱模式需要此參數(shù))
    "warmUpPeriodSec": 10,
    // 超時(shí)時(shí)間(排隊(duì)等待模式需要此參數(shù))
    "maxQueueingTimeMs": 500,
    // 關(guān)聯(lián)資源、入口資源(關(guān)聯(lián)藤乙、鏈路模式)
    "refResource": "rrr"
  }
]

熔斷規(guī)則

[
  {
      // 資源名
    "resource": "/test1",
    "limitApp": "default",
    // 熔斷策略(0:慢調(diào)用比例猜揪,1:異常比率,2:異常計(jì)數(shù))
    "grade": 0,
    // 最大RT坛梁、比例閾值而姐、異常數(shù)
    "count": 200,
    // 慢調(diào)用比例閾值,僅慢調(diào)用比例模式有效(1.8.0 引入)
    "slowRatioThreshold": 0.2,
    // 最小請(qǐng)求數(shù)
    "minRequestAmount": 5,
    // 當(dāng)單位統(tǒng)計(jì)時(shí)長(zhǎng)(類中默認(rèn)1000)
    "statIntervalMs": 1000,
    // 熔斷時(shí)長(zhǎng)
    "timeWindow": 10
  }
]

熱點(diǎn)規(guī)則

[
  {
    // 資源名
    "resource": "/test1",
    // 限流模式(QPS 模式划咐,不可更改)
    "grade": 1,
    // 參數(shù)索引
    "paramIdx": 0,
    // 單機(jī)閾值
    "count": 13,
    // 統(tǒng)計(jì)窗口時(shí)長(zhǎng)
    "durationInSec": 6,
    // 是否集群 默認(rèn)false
    "clusterMode": false,
    //
    "burstCount": 0,
    // 集群模式配置
    "clusterConfig": {
      //
      "fallbackToLocalWhenFail": true,
      //
      "flowId": 2,
      //
      "sampleCount": 10,
      //
      "thresholdType": 0,
      //
      "windowIntervalMs": 1000
    },
    // 流控效果(支持快速失敗和勻速排隊(duì)模式)
    "controlBehavior": 0,
    //
    "limitApp": "default",
    //
    "maxQueueingTimeMs": 0,
    // 高級(jí)選項(xiàng)
    "paramFlowItemList": [
      {
        // 參數(shù)類型
        "classType": "int",
        // 限流閾值
        "count": 222,
        // 參數(shù)值
        "object": "2"
      }
    ]
  }
]

系統(tǒng)規(guī)則

  [
    {
      // RT
      "avgRt": 1,
      // CPU 使用率
      "highestCpuUsage": -1,
      // LOAD
      "highestSystemLoad": -1,
      // 線程數(shù)
      "maxThread": -1,
      // 入口 QPS
      "qps": -1
    }
  ]

授權(quán)規(guī)則

[
  {
    // 資源名
    "resource": "sentinel_spring_web_context",
    // 流控應(yīng)用
    "limitApp": "/test",
    // 授權(quán)類型(0代表白名單拴念;1代表黑名單钧萍。)
    "strategy": 0
  }
]

各類規(guī)則對(duì)應(yīng)的yml配置

spring:
  application.name: SentinelService
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8082  #Nacos 服務(wù)地址
    sentinel:
      log:
        dir: /logs
        switch-pid: true
      transport:
        dashboard: 127.0.0.1:8080
      datasource:
        flow: #流控規(guī)則
          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: #熔斷規(guī)則
          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: #系統(tǒng)規(guī)則
          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: #授權(quán)規(guī)則
          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: #熱點(diǎn)參數(shù)規(guī)則
          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

以上提供了五種規(guī)則的nacos配置及yml配置,可以完整的支持sentinel規(guī)則配置持久化功能政鼠!

如有問(wèn)題歡迎評(píng)論留言溝通风瘦!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市公般,隨后出現(xiàn)的幾起案子万搔,更是在濱河造成了極大的恐慌,老刑警劉巖官帘,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞬雹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡刽虹,警方通過(guò)查閱死者的電腦和手機(jī)酗捌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涌哲,“玉大人胖缤,你說(shuō)我怎么就攤上這事√懦妫” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵钓猬,是天一觀的道長(zhǎng)稍刀。 經(jīng)常有香客問(wèn)我,道長(zhǎng)敞曹,這世上最難降的妖魔是什么账月? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮澳迫,結(jié)果婚禮上局齿,老公的妹妹穿的比我還像新娘。我一直安慰自己橄登,他們只是感情好抓歼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拢锹,像睡著了一般谣妻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卒稳,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天蹋半,我揣著相機(jī)與錄音,去河邊找鬼充坑。 笑死减江,一個(gè)胖子當(dāng)著我的面吹牛染突,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辈灼,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼份企,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了茵休?” 一聲冷哼從身側(cè)響起薪棒,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榕莺,沒(méi)想到半個(gè)月后俐芯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钉鸯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年吧史,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唠雕。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贸营,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出岩睁,到底是詐尸還是另有隱情钞脂,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布捕儒,位于F島的核電站冰啃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏刘莹。R本人自食惡果不足惜阎毅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望点弯。 院中可真熱鬧扇调,春花似錦、人聲如沸抢肛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捡絮。三九已至燃领,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锦援,已是汗流浹背猛蔽。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人曼库。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓区岗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親毁枯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慈缔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容