1.导入sentinel和nacos 依赖

<dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        <version>2.2.8.RELEASE</version>
    </dependency>
    <!--    sentinel的依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        <version>2.2.8.RELEASE</version>
    </dependency>

2.新建对应sentinel限流、熔断值对象

```javascript
@Data
public class GatewaySentinelConfig {
    /**
     * 资源名称
     */
    private String resource;

    /**
     * sentinel限流类型:1-api分组限流;0-路由模式限流
     */
    private Integer resourceMode;

    /**
     * 限流熔断阈值
     */
    private Double count;

    /**
     * 熔断策略类型;DEGRADE_GRADE_RT = 0:慢调用比例;
     * DEGRADE_GRADE_EXCEPTION_RATIO = 1:异常比例
     * DEGRADE_GRADE_EXCEPTION_COUNT = 2:异常数
     */
    private Integer grade;

    /**
     * 熔断时长,单位为 s
     */
    private Integer timeWindow;

    /**
     * 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断。
     *
     * 默认值:5
     */
    private Integer minRequestAmount;

    /*
     * 统计时长(单位为 ms),如 60*1000 代表分钟级。
     * 默认:1000 ms
     * 注意与上面count值得联系,同时该时间段内的请求都会进入并参与统计。
     * */
    private Integer statIntervalMs;

    /*
     *慢调用比例阈值,仅慢调用比例模式有效
     * */
    private Double slowRatioThreshold;
}

3.实现CommandLineRunner接口,监听nacos变化,将更新后的规则重新加载进sentinel中,使用@Value去读取不同环境的nacos相关配置

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.cmcc.coc3.gateway.meta.route.client.GatewayInterfaceCache;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.Executor;

@Component
@Slf4j
public class DynamicFlowRulesServiceImplByNacos implements CommandLineRunner {
    @Value("${spring.cloud.nacos.discovery.server-addr}")
    private String naocsAddress;

    @Value("${gatewayRoute.namespaceId}")
    private String nacosNamespaceId;

    @Value("${gatewayRoute.nacosDateId}")
    private String nacosDataId;

    @Value("${gatewayRoute.groupId}")
    private String nacosGroupId;

    @Override
    public void run(String... args) throws Exception {
        dynamicByNacosListener();
    }
    private ConfigService configService;


    /**
     * 监听Nacos Server下发的动态配置
     */
    public void dynamicByNacosListener() {
        if (configService == null) {
            try {
                Properties properties = new Properties();
                properties.put(PropertyKeyConst.SERVER_ADDR, naocsAddress);
                properties.put(PropertyKeyConst.NAMESPACE,nacosNamespaceId);
                configService = NacosFactory.createConfigService(properties);
            } catch (NacosException e) {
                e.printStackTrace();
            }
        }
        sentinelConfigByNacosListener();
    }

    /**
     * 网关动态配置基础信息
     */
    public void sentinelConfigByNacosListener() {
        try {
            String fristConfigInfo = configService.getConfigAndSignListener(nacosDataId, nacosGroupId, 5000, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                //nacos的限流值变化时,将会执行里面的方法
                    addListenerByConfig(nacosDataId, nacosGroupId, configInfo);
                }

                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
            addListenerByConfig(nacosDataId, nacosGroupId, fristConfigInfo);
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     */

    public void addListenerByConfig(String dataId, String groupId, String configInfo) {
        log.info("DataId:" + dataId + ",GroupId:" + groupId + ",监听器获取的nacos-sentinel流控配置信息:" + configInfo);
        List<GatewaySentinelConfig> gatewaySentinelConfigs = null;
        try {
            gatewaySentinelConfigs = new Gson().fromJson(configInfo, new TypeToken<List<GatewaySentinelConfig>>() {
            }.getType());
        } catch (JsonSyntaxException e) {
            log.error("JsonSyntaxException", e, e.getMessage());
        }
        if (gatewaySentinelConfigs == null || gatewaySentinelConfigs.size() == 0) {
            return;
        }
        //加载流控规则
        initGatewaySentinelRules(gatewaySentinelConfigs);
    }

    public void initGatewaySentinelRules(List<GatewaySentinelConfig> gatewaySentinelConfigs) {
        Set<GatewayFlowRule> rules = new HashSet<>();
        Set<ApiDefinition> definitions = new HashSet<>();
        for (GatewaySentinelConfig sentinelConfig : gatewaySentinelConfigs) {
            //加载限流规则
            rules.add(new 
            //注意,使用限流时资源名称,可以随便定义,我这里是使用的路由的接口路径,资源名称前缀不能带有特殊字符/,否则sentinel校验规则时,限流值会不准确
            GatewayFlowRule(sentinelConfig.getResource())//资源名称
                    .setResourceMode(sentinelConfig.getResourceMode())//资源类型
                    .setCount(sentinelConfig.getCount())  // 限流阈值
                    .setIntervalSec(1)  // 统计时间窗口,单位是秒,默认是 1 秒
            );
            if (sentinelConfig.getResourceMode() == 1) {  //自定义API模式
                ApiDefinition api1 = new ApiDefinition(sentinelConfig.getResource()).setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    if (StringUtils.isNotBlank(sentinelConfig.getPattern())) {
                        //精准匹配,用于API分组限流模式
                        add(new ApiPathPredicateItem().setPattern(sentinelConfig.getPattern()).setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));
                    }
                }});
                definitions.add(api1);
            }
        }
        GatewayRuleManager.loadRules(rules);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
}

这里就完成了sentinel的规则加载代码

4.编写配置类,自定义返回异常信息,实现BlockRequestHandler接口

import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpStatus;

import java.util.*;

@Configuration
public class SentinelGatewayConfig  implements BlockRequestHandler {
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public SentinelGatewayConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                 ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

	//这是执行sentinel规则的拦截器,Gateway路由之前会先执行这个拦截器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @Override
    public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
        String path = serverWebExchange.getRequest().getPath().pathWithinApplication().value();
        Map map = new HashMap();
        map.put("error","Unauthorized");
        map.put("path",path);
        map.put("status",HttpStatus.TOO_MANY_REQUESTS.value());
        map.put("imestamp",new Date());
        if (throwable instanceof FlowException){
            map.put("message","服务被限流");
        } else if (throwable instanceof DegradeException) {
            map.put("message","服务被熔断降级");
        }
        return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS.value()).contentType(MediaType.APPLICATION_JSON).body(Mono.just(map), Map.class);
    }
}

5.nacos中限流配置

Resilience4J限流集成到SpringCloud Gateway中_限流

代码中将修改后的限流配置推送到nacos中

先引入nacos依赖,然后在类中添加@Value注解,去读取不同环境的nacos配置,将用于去创建nacos链接

<dependency>
                <groupId>com.alibaba.nacos</groupId>
                <artifactId>nacos-client</artifactId>
                <version>1.4.3</version>
            </dependency>



@Value("${spring.cloud.nacos.discovery.server-addr}")
    private String naocsAddress;

    @Value("${gatewayRoute.namespaceId}")
    private String nacosNamespaceId;

    @Value("${gatewayRoute.nacosDateId}")
    private String nacosDataId;

    @Value("${gatewayRoute.groupId}")
    private String nacosGroupId;
public void pullNaocs(String limitValue){
        log.info("开始执行nacos推送");
        if (configService == null) {
            log.info("configService为空,开始创建nacos链接");
            try {
                Properties properties = new Properties();
                properties.put(PropertyKeyConst.SERVER_ADDR, naocsAddress);
                properties.put(PropertyKeyConst.NAMESPACE,nacosNamespaceId);
                configService = NacosFactory.createConfigService(properties);
            } catch (NacosException e) {
                e.printStackTrace();
                log.error("创建nacos链接出现未知异常:{}",e.getErrMsg());
            }
        }
        boolean b = false;
        try {
        //GatewaySentinelConfigReq与sentinel限流配置相对应
            List<GatewaySentinelConfigReq> configReqList = jsonToList(limitValue,GatewaySentinelConfigReq.class);
            String json = new Gson().toJson(configReqList);
            b = configService.publishConfig(nacosDataId, nacosGroupId, json);
            log.info("推送新的配置,新的配置是:{},推送是否成功:{}",limitValue,b);
        } catch (NacosException e) {
            e.printStackTrace();
            log.error("推送nacos配置出现未知异常:{}",e.getErrMsg());
        }
    }

    public static <T> List<T> jsonToList(String jsonString, Class<T> clz){
        Gson gson = new Gson();
        JsonArray jsonArray = gson.fromJson(jsonString, JsonArray.class);
        if (jsonArray.isJsonNull())return null;
        List<T>beans = new ArrayList<>(jsonArray.size());
        jsonArray.forEach(netJson->beans.add(new Gson().fromJson(netJson.toString(),clz)));
        return beans;
    }