SpringCloud Alibaba 改造Sentinel Dashboard将熔断规则持久化到Nacos


Sentinel Dashboard集成Nacos目录:

SpringCloud Alibaba 改造Sentinel Dashboard将流控规则持久化到Nacos

SpringCloud Alibaba 改造Sentinel Dashboard将熔断规则持久化到Nacos  本文

在《SpringCloud Alibaba 改造Sentinel Dashboard将流控规则持久化到Nacos》介绍了如何修改Sentinel Dashboard的源代码,使得通过Sentinel Dashboard维护的流控规则自动持久化到Nacos上,应用程序通过订阅Nacos上的配置实现流量控制。

本文接着介绍如何修改源码实现熔断规则的持久化。

二. Sentinel Dashboard集成Nacos实现熔断规则持久化

同前文介绍的集成Nacos实现流控规则持久化类似,为了实现熔断规则持久化,其大致实现步骤仍然是:

  1. 创建新的实现类,实现DynamicRuleProvider接口和DynamicRulePublisher接口。
  2. 修改对应的Controller,注入新的实现类。

2.1 准备工作

为了减少代码,更加规范写法,统一流控规则和熔断规则的持久化的写法,首先对之前流控规则的代码予以部分修改,新的包结构如下:

以下是代码明细:

NacosConfiguration.java

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

import java.util.Properties;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;

/**
 * Nacos配置类
 * @author gang.wang
 * 2021年10月31日
 */
@EnableConfigurationProperties(NacosPropertiesConfiguration.class)
@Configuration
public class NacosConfiguration {
    
    @Bean
    public ConfigService nacosConfigService(NacosPropertiesConfiguration nacosPropertiesConfiguration) throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfiguration.getServerAddr());
        properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfiguration.getNamespace());
        return ConfigFactory.createConfigService(properties);
    }
}

NacosConstants.java

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

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;

import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;

/**
 * Nacos常量类
 * @author gang.wang
 * 2021年11月8日
 */
public class NacosConstants {
    
    private static Logger logger = LoggerFactory.getLogger(NacosConstants.class);
    
    public static final String GROUP_ID = "DEFAULT_GROUP";
    
    /**
     * 流控规则后缀
     */
    public static final String FLOW_DATA_ID_POSTFIX = "-sentinel-flow";
    
    /**
     * 熔断规则后缀
     */
    public static final String DEGRADE_DATA_ID_POSTFIX = "-sentinel-degrade";
    
    /**
     * 从Nacos server中查询响应规则,并将其反序列化成对应Rule实体
     *
     * @param configService nacos config service
     * @param groupId       组ID
     * @param dataId        Nacos DataId
     * @param clazz         类
     * @param            泛型
     * @return 规则对象列表
     * @throws NacosException 异常
     */
    public static  List getRuleEntitiesFromNacos(ConfigService configService, 
            String groupId, String dataId, Class clazz) throws NacosException {
        String rules = configService.getConfig(dataId, groupId, 3000);
        
        logger.info("Pull Rule from Nacos Config : {}", rules);
        
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return JSON.parseArray(rules, clazz);
    }
    
    /**
     * 将规则序列化成为JSON信息,并发布到Nacos上
     * @param 
     * @param configService
     * @param groupId
     * @param dataId
     * @param ruleEntities
     * @return
     * @throws NacosException
     */
    @Bean
    public static  Boolean setRuleEntitiesFromNacos(ConfigService configService, 
            String groupId, String dataId, List ruleEntities) throws NacosException {
        String ruleEntitiesStr = JSON.toJSONString(ruleEntities);
        logger.info("Push Rule to Nacos Config : {}", ruleEntitiesStr);
        
        return configService.publishConfig(dataId, groupId, ruleEntitiesStr);
    }
    
}

NacosPropertiesConfiguration.java的内容依然保持不变:

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

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

/**
 * 加载Nacos配置
 * @author gang.wang
 * 2021年10月31日
 */
@ConfigurationProperties(prefix="sentinel.nacos")
public class NacosPropertiesConfiguration {
    
    /**
     * Nacos服务地址
     */
    private String serverAddr;
    
    private String dataId;
    
    private String groupId = "DEFAULT_GROUP";
    
    private String namespace;

    public String getServerAddr() {
        return serverAddr;
    }

    public void setServerAddr(String serverAddr) {
        this.serverAddr = serverAddr;
    }

    public String getDataId() {
        return dataId;
    }

    public void setDataId(String dataId) {
        this.dataId = dataId;
    }

    public String getGroupId() {
        return groupId;
    }

    public void setGroupId(String groupId) {
        this.groupId = groupId;
    }

    public String getNamespace() {
        return namespace;
    }

    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }
    
}

FlowRuleNacosProvider.java

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

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConstants;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosPropertiesConfiguration;
import com.alibaba.nacos.api.config.ConfigService;

/**
 * 实现从Nacos配置中心获取流控规则
 * @author gang.wang
 * 2021年11月8日
 */
@Service
public class FlowRuleNacosProvider implements DynamicRuleProvider> {
    
    @Autowired
    private NacosPropertiesConfiguration nacosConfigProperties;
    
    @Autowired
    private ConfigService configService;
    
    @Override
    public List getRules(String appName) throws Exception {
        
        //定义dataId 应用名+固定后缀
        String dataId = new StringBuilder(appName).append(NacosConstants.FLOW_DATA_ID_POSTFIX).toString();
        
        List list = NacosConstants.getRuleEntitiesFromNacos(configService, nacosConfigProperties.getGroupId(), dataId, FlowRuleEntity.class);
        
        return list;
    }
}

FlowRuleNacosPublisher.java

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

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConstants;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosPropertiesConfiguration;
import com.alibaba.nacos.api.config.ConfigService;

/**
 * 将通过Sentinel Dashboard上维护的流控规则数据持久化到Nacos中
 * @author gang.wang
 * 2021年11月8日
 */
@Service
public class FlowRuleNacosPublisher implements DynamicRulePublisher> {
    
    private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosPublisher.class);
    
    @Autowired
    private NacosPropertiesConfiguration nacosConfigProperties;
    
    @Autowired
    private ConfigService configService;
    
    @Override
    public void publish(String appName, List rules) throws Exception {
        
        if(StringUtils.isBlank(appName)) {
            logger.error("传入的AppName为Null");
            return ;
        }
        
        if(null == rules) {
            logger.error("传入的流控规则数据为null");
            return ;
        }
        
        String dataId = new StringBuilder(appName).append(NacosConstants.FLOW_DATA_ID_POSTFIX).toString();
        
        NacosConstants.setRuleEntitiesFromNacos(configService, nacosConfigProperties.getGroupId(), dataId, rules);
    }
}

2.2 熔断规则持久化部分代码

2.2.1 分别创建类实现DynamicRuleProvider接口和DynamicRulePublisher接口

实现熔断规则持久化的代码和之前流控规则的类似,都需要创建两个类分别实现DynamicRuleProvider接口和DynamicRulePublisher接口,只不过接口的泛型不同,熔断规则的为:List

在com.alibaba.csp.sentinel.dashboard.rule.nacos包下创建degrade包。创建类:DegradeRuleNacosProvider.java和DegradeRuleNacosPublisher.java,分别实现<从Nacos拉取规则并展示在Dashboard上>和<将通过Dashboard维护的熔断规则持久化到Nacos上>这两个功能。

二者代码实现也同流控规则中的代码类似。

DegradeRuleNacosProvider.java

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

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConstants;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosPropertiesConfiguration;
import com.alibaba.nacos.api.config.ConfigService;

/**
 * 实现从Nacos配置中心获取熔断规则
 * @author gang.wang
 * 2021年11月15日
 */
@Service
public class DegradeRuleNacosProvider implements DynamicRuleProvider> {
    
    @Autowired
    private NacosPropertiesConfiguration nacosConfigProperties;
    
    @Autowired
    private ConfigService configService;
    
    @Override
    public List getRules(String appName) throws Exception {
        
        //定义dataId 应用名+固定后缀
        String dataId = new StringBuilder(appName).append(NacosConstants.DEGRADE_DATA_ID_POSTFIX).toString();
        
        List list = NacosConstants.getRuleEntitiesFromNacos(configService, nacosConfigProperties.getGroupId(), dataId, DegradeRuleEntity.class);
        
        return list;
    }
}

DegradeRuleNacosPublisher.java

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

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConstants;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosPropertiesConfiguration;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.flow.FlowRuleNacosPublisher;
import com.alibaba.nacos.api.config.ConfigService;

/**
 * 实现将熔断规则持久化到Nacos中
 * @author gang.wang
 * 2021年11月15日
 */
@Service
public class DegradeRuleNacosPublisher implements DynamicRulePublisher> {

private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosPublisher.class);
    
    @Autowired
    private NacosPropertiesConfiguration nacosConfigProperties;
    
    @Autowired
    private ConfigService configService;
    
    @Override
    public void publish(String appName, List rules) throws Exception {
        
        if(StringUtils.isBlank(appName)) {
            logger.error("传入的AppName为Null");
            return ;
        }
        
        if(null == rules) {
            logger.error("传入的熔断规则数据为null");
            return ;
        }
        
        String dataId = new StringBuilder(appName).append(NacosConstants.DEGRADE_DATA_ID_POSTFIX).toString();
        
        NacosConstants.setRuleEntitiesFromNacos(configService, nacosConfigProperties.getGroupId(), dataId, rules);
    }

}

2.2.2 修改com.alibaba.csp.sentinel.dashboard.controller.DegradeController,注入新的实现类

此Controller中只需要修改两个方法即可,分别是:

  1. com.alibaba.csp.sentinel.dashboard.controller.DegradeController.apiQueryMachineRules(String, String, Integer)
  2. com.alibaba.csp.sentinel.dashboard.controller.DegradeController.publishRules(String, String, Integer)

修改后的代码如下:

/*
 * 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.controller;

import java.util.Date;
import java.util.List;

import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import com.alibaba.csp.sentinel.util.StringUtil;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.domain.Result;

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.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Controller regarding APIs of degrade rules. Refactored since 1.8.0.
 *
 * @author Carpenter Lee
 * @author Eric Zhao
 */
@RestController
@RequestMapping("/degrade")
public class DegradeController {

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

    /**
     * 将规则加载到Sentinel Dashboard的内存中
     */
    @Autowired
    private RuleRepository repository;
    
    @Autowired
    @Qualifier("degradeRuleNacosProvider")
    private DynamicRuleProvider> ruleProvider;
    @Autowired
    @Qualifier("degradeRuleNacosPublisher")
    private DynamicRulePublisher> rulePublisher;
    
    @Autowired
    private SentinelApiClient sentinelApiClient;

    @GetMapping("/rules.json")
    @AuthAction(PrivilegeType.READ_RULE)
    public Result> apiQueryMachineRules(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.fetchDegradeRuleOfMachine(app, ip, port);
            //修改为从Nacos中加载熔断规则
            List rules = ruleProvider.getRules(app);
            rules = repository.saveAll(rules);
            return Result.ofSuccess(rules);
        } catch (Throwable throwable) {
            logger.error("queryApps error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }
    }

    @PostMapping("/rule")
    @AuthAction(PrivilegeType.WRITE_RULE)
    public Result apiAddRule(@RequestBody DegradeRuleEntity entity) {
        Result checkResult = checkEntityInternal(entity);
        if (checkResult != null) {
            return checkResult;
        }
        Date date = new Date();
        entity.setGmtCreate(date);
        entity.setGmtModified(date);
        try {
            entity = repository.save(entity);
        } catch (Throwable t) {
            logger.error("Failed to add new degrade rule, app={}, ip={}", entity.getApp(), entity.getIp(), t);
            return Result.ofThrowable(-1, t);
        }
        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
            logger.warn("Publish degrade rules failed, app={}", entity.getApp());
        }
        return Result.ofSuccess(entity);
    }

    @PutMapping("/rule/{id}")
    @AuthAction(PrivilegeType.WRITE_RULE)
    public Result apiUpdateRule(@PathVariable("id") Long id,
                                                     @RequestBody DegradeRuleEntity entity) {
        if (id == null || id <= 0) {
            return Result.ofFail(-1, "id can't be null or negative");
        }
        DegradeRuleEntity oldEntity = repository.findById(id);
        if (oldEntity == null) {
            return Result.ofFail(-1, "Degrade rule does not exist, id=" + id);
        }
        entity.setApp(oldEntity.getApp());
        entity.setIp(oldEntity.getIp());
        entity.setPort(oldEntity.getPort());
        entity.setId(oldEntity.getId());
        Result checkResult = checkEntityInternal(entity);
        if (checkResult != null) {
            return checkResult;
        }

        entity.setGmtCreate(oldEntity.getGmtCreate());
        entity.setGmtModified(new Date());
        try {
            entity = repository.save(entity);
        } catch (Throwable t) {
            logger.error("Failed to save degrade rule, id={}, rule={}", id, entity, t);
            return Result.ofThrowable(-1, t);
        }
        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
            logger.warn("Publish degrade rules failed, app={}", entity.getApp());
        }
        return Result.ofSuccess(entity);
    }

    @DeleteMapping("/rule/{id}")
    @AuthAction(PrivilegeType.DELETE_RULE)
    public Result delete(@PathVariable("id") Long id) {
        if (id == null) {
            return Result.ofFail(-1, "id can't be null");
        }

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

        try {
            repository.delete(id);
        } catch (Throwable throwable) {
            logger.error("Failed to delete degrade rule, id={}", id, throwable);
            return Result.ofThrowable(-1, throwable);
        }
        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
            logger.warn("Publish degrade rules failed, app={}", oldEntity.getApp());
        }
        return Result.ofSuccess(id);
    }

    private boolean publishRules(String app, String ip, Integer port) {
        List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
        //注释掉原有代码
        //return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);
        try {
            rulePublisher.publish(app, rules);
            return true;
        } catch(Exception ex) {
            logger.error("Publish degrade rules failed", ex);
            return false;
        }
    }

    private  Result checkEntityInternal(DegradeRuleEntity entity) {
        if (StringUtil.isBlank(entity.getApp())) {
            return Result.ofFail(-1, "app can't be blank");
        }
        if (StringUtil.isBlank(entity.getIp())) {
            return Result.ofFail(-1, "ip can't be null or empty");
        }
        if (entity.getPort() == null || entity.getPort() <= 0) {
            return Result.ofFail(-1, "invalid port: " + entity.getPort());
        }
        if (StringUtil.isBlank(entity.getLimitApp())) {
            return Result.ofFail(-1, "limitApp can't be null or empty");
        }
        if (StringUtil.isBlank(entity.getResource())) {
            return Result.ofFail(-1, "resource can't be null or empty");
        }
        Double threshold = entity.getCount();
        if (threshold == null || threshold < 0) {
            return Result.ofFail(-1, "invalid threshold: " + threshold);
        }
        Integer recoveryTimeoutSec = entity.getTimeWindow();
        if (recoveryTimeoutSec == null || recoveryTimeoutSec <= 0) {
            return Result.ofFail(-1, "recoveryTimeout should be positive");
        }
        Integer strategy = entity.getGrade();
        if (strategy == null) {
            return Result.ofFail(-1, "circuit breaker strategy cannot be null");
        }
        if (strategy < CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType()
            || strategy > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
            return Result.ofFail(-1, "Invalid circuit breaker strategy: " + strategy);
        }
        if (entity.getMinRequestAmount()  == null || entity.getMinRequestAmount() <= 0) {
            return Result.ofFail(-1, "Invalid minRequestAmount");
        }
        if (entity.getStatIntervalMs() == null || entity.getStatIntervalMs() <= 0) {
            return Result.ofFail(-1, "Invalid statInterval");
        }
        if (strategy == RuleConstant.DEGRADE_GRADE_RT) {
            Double slowRatio = entity.getSlowRatioThreshold();
            if (slowRatio == null) {
                return Result.ofFail(-1, "SlowRatioThreshold is required for slow request ratio strategy");
            } else if (slowRatio < 0 || slowRatio > 1) {
                return Result.ofFail(-1, "SlowRatioThreshold should be in range: [0.0, 1.0]");
            }
        } else if (strategy == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) {
            if (threshold > 1) {
                return Result.ofFail(-1, "Ratio threshold should be in range: [0.0, 1.0]");
            }
        }
        return null;
    }
}

2.3 测试通过Sentinel Dashboard维护的熔断规则是否可以持久化到Nacos中

访问Sentinel Dashboard,维护一条熔断规则,示例如下:

为了后面验证简单,这里我们定义的熔断规则的含义是:此接口在1000ms内当至少有3次访问,并至少有2次异常,则此接口熔断10秒。

刷新列表可以看到刚刚维护的信息:

 访问Nacos,查看是否正常存储:

 

 由上可见,通过Sentinel Dashboard维护的熔断规则已经正常持久化到Nacos中了。

2.4 应用程序接入

应用程序需要订阅Nacos上的对应DataId,当熔断规则有变化时,Nacos会自动推送到已经接入的应用程序上。其具体接入的方式同之前介绍的流控规则类似。

首先在application.yml中添加对应的配置。application.yml中与Sentinel相关的配置如下:

spring:
    sentinel:
      transport: 
        dashboard: 127.0.0.1:8080
      datasource: 
        flow: 
          nacos: 
            server-addr: 127.0.0.1:8848
            namespace: 37c7c263-bdf1-41db-9f34-bf10948be752
            data-id: ${spring.application.name}-sentinel-flow
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow
        degrade: 
          nacos: 
            server-addr: 127.0.0.1:8848
            namespace: 37c7c263-bdf1-41db-9f34-bf10948be752
            data-id: ${spring.application.name}-sentinel-degrade
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: degrade

为了配合上面定义的基于异常数的熔断规则,我们修改之前定义的接口,抛出任意异常即可。

@SentinelResource(value = "hello", blockHandler = "blockHandlerHello")
@GetMapping("/say")
public String hello() {
    int a = 10;
    int b = 0;
    int c = a / b;
    return "hello, Gary!";
}

启动应用程序,并利用Postman并发访问刚刚我们定义了熔断规则的接口。

可以看到前两次接口访问报500错误,从第三次开始http code就开始等于200,responseBody的值为当前请求已被限流。所以可见我们定义的熔断规则生效了。

至此熔断规则持久化到Nacos的功能就已完成了!