第十五章:Spring Cloud微服务架构下:Nacos动态配置Sentinel规则与OpenFeign集成的全链路限流实践

牛子 | 2025-11-17 22:07:01 | 87 | 0 | 0 | 0

一、前言

上篇文章已经讲了怎么在docker中部署sentinel,创建一个测试demo怎样去实现限流,这篇文章的话是怎么去使用后端接口动态上传限流规则到nacos,sentinel去动态加载限流规则去进行限流,和使用openfegin进行一个跨服务的一个限流

二、实现流程

2.1、创建json-service

424bdd2532db4b5aa1352ad45a4b1cfe.png

2.2、创建一个nacos的config

package com.ruoyi.vehicle.config;
 
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import java.util.Properties;
 
@Configuration
public class NacosConfigServiceConfig {
 
    private final NacosConfigProperties properties;
 
    public NacosConfigServiceConfig(NacosConfigProperties properties) {
        this.properties = properties;
    }
 
    @Bean
    public ConfigService nacosConfigService() throws NacosException {
        Properties props = new Properties();
        props.put("serverAddr", this.properties.getServerAddr());
        
        // 只有当用户名和密码非空时才设置
        if (this.properties.getUsername() != null && !this.properties.getUsername().isEmpty()) {
            props.put("username", this.properties.getUsername());
        }
        if (this.properties.getPassword() != null && !this.properties.getPassword().isEmpty()) {
            props.put("password", this.properties.getPassword());
        }
        
        return NacosFactory.createConfigService(props);
    }
}

2.3、redis config

package com.ruoyi.vehicle.config;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
 
@Configuration
public class RedisConfig {
 
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用StringRedisSerializer序列化key
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        
        // 使用Jackson2JsonRedisSerializer序列化value
        template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
        template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
        
        template.afterPropertiesSet();
        return template;
    }
}

2.4、创建controller实现创建api分组和获取分组

package com.ruoyi.vehicle.controller;
 
import com.alibaba.nacos.api.exception.NacosException;
import com.ruoyi.vehicle.DTO.ApiGroupRequest;
import com.ruoyi.vehicle.DTO.ApiResponse;
import com.ruoyi.vehicle.service.ApiGroupService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
 
@Slf4j
@RestController
@RequestMapping("/api-groups")
public class ApiGroupController {
 
    private final ApiGroupService apiGroupService;
 
    @Autowired
    public ApiGroupController(ApiGroupService apiGroupService) {
        this.apiGroupService = apiGroupService;
    }
 
    /**
     * 创建API分组
     */
    @PostMapping("/create")
    public ApiResponse<String> createApiGroup(@Validated @RequestBody ApiGroupRequest request) {
        try {
            String dataId = apiGroupService.createApiGroup(request);
            return ApiResponse.success(dataId);
        } catch (NacosException e) {
            log.error("Nacos配置失败", e);
            return ApiResponse.error(500, "Nacos配置失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("创建API分组失败", e);
            return ApiResponse.error(500, "创建API分组失败: " + e.getMessage());
        }
    }
 
    /**
     * 获取API分组详情
     */
    @GetMapping("/{groupCode}")
    public ApiResponse<String> getApiGroup(@PathVariable String groupCode) {
        try {
            String dataId = "api-groups-" + groupCode;
            String content = apiGroupService.getConfigContent(dataId);
            return ApiResponse.success(content);
        } catch (Exception e) {
            log.error("获取API分组失败", e);
            return ApiResponse.error(500, "获取API分组失败: " + e.getMessage());
        }
    }
}

2.5、创建json的controller

package com.ruoyi.vehicle.controller;
 
 
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
 
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@RestController
@RequestMapping("/api/json")
public class JsonController {
 
    @PostMapping("/parse")
    public ResponseEntity<Map<String, Object>> parseJson(@RequestBody String jsonString) {
        // 实际项目中会调用JsonService,这里简化演示
        Map<String, Object> result = Map.of("parsed", jsonString, "status", "success");
        return ResponseEntity.ok(result);
    }
 
    @PostMapping("/batch-parse")
    public ResponseEntity<List<Map<String, Object>>> batchParseJson(@RequestBody List<String> jsonList) {
        // 实际项目中会调用JsonService,这里简化演示
        List<Map<String, Object>> results = jsonList.stream()
                .map(json -> {
                    Map<String, Object> result = new HashMap<>();
                    result.put("parsed", json);
                    result.put("status", "success");
                    return result;
                })
                .toList();
 
        return ResponseEntity.ok(results);
    }
 
}

2.6、创建普通添加策略、批量添加策略、获取策略

package com.ruoyi.vehicle.controller;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.exception.NacosException;
import com.ruoyi.vehicle.DTO.ApiResponse;
import com.ruoyi.vehicle.DTO.StrategyDetail;
import com.ruoyi.vehicle.DTO.StrategyRequest;
import com.ruoyi.vehicle.Sentinel.GatewayFlowRule;
import com.ruoyi.vehicle.service.StrategyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
 
import java.util.Date;
import java.util.List;
 
@Slf4j
@RestController
@RequestMapping("/strategies")
public class StrategyController {
 
    private final StrategyService strategyService;
 
    @Autowired
    public StrategyController(StrategyService strategyService) {
        this.strategyService = strategyService;
    }
 
    /**
     * 创建监控策略
     */
    @PostMapping("/create")
    public ApiResponse<String> createStrategy(@Validated @RequestBody StrategyRequest request) {
        try {
            String dataId = strategyService.createStrategy(request);
            return ApiResponse.success(dataId);
        } catch (NacosException e) {
            log.error("Nacos配置失败", e);
            return ApiResponse.error(500, "Nacos配置失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("创建策略失败", e);
            return ApiResponse.error(500, "创建策略失败: " + e.getMessage());
        }
    }
 
    /**
     * 批量创建策略
     */
    @PostMapping("/create/batch")
    public ApiResponse<String> createMultipleStrategies(@Validated @RequestBody List<StrategyRequest> requests) {
        try {
            strategyService.createMultipleStrategies(requests);
            return ApiResponse.success("批量策略创建成功");
        } catch (NacosException e) {
            log.error("Nacos配置失败", e);
            return ApiResponse.error( 500,  "Nacos配置失败:" + e.getMessage());
        } catch (Exception e) {
            log.error("批量创建策略失败", e);
            return ApiResponse.error( 500,  "批量创建策略失败:" + e.getMessage());
        }
    }
 
    /**
     * 获取策略详情
     */
    @GetMapping("/{configCode}")
    public ApiResponse<String> getStrategy(@PathVariable String configCode) {
        try {
            String dataId = "gateway-flow-rules-" + configCode;
            String content = strategyService.getConfigContent(dataId);
 
            // 解析 JSON 为对象
            List<GatewayFlowRule> rules = JSON.parseArray(content, GatewayFlowRule.class);
 
            StrategyDetail detail = new StrategyDetail();
            detail.setConfigCode(configCode);
            detail.setRules(rules);
            detail.setUpdateTime(new Date());
            return ApiResponse.success(content);
        } catch (Exception e) {
            log.error("获取策略失败", e);
            return ApiResponse.error(500, "获取策略失败: " + e.getMessage());
        }
    }
}

2.7、创建限流规则

package com.ruoyi.vehicle.controller;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.exception.NacosException;
import com.ruoyi.vehicle.DTO.ApiResponse;
import com.ruoyi.vehicle.DTO.StrategyRequest;
import com.ruoyi.vehicle.DTO.Thresholds;
import com.ruoyi.vehicle.service.StrategyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.Arrays;
 
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
 
    private final StrategyService strategyService;
 
    @Autowired
    public TestController(StrategyService strategyService) {
        this.strategyService = strategyService;
    }
 
    /**
     * 创建针对具体API路径的流控规则示例
     */
    @PostMapping("/createApiRule")
    public ApiResponse<String> createApiRule() {
        try {
            StrategyRequest request = new StrategyRequest();
            request.setStrategyName("API路径流控策略");
            request.setMonitoringType("FLOW_CONTROL");
            request.setControlMeasure("限流");
            request.setConfigCode("api-test-rule");
            request.setRoutePath("/test/flow-control"); // 具体API路径
            request.setResourceMode("API"); // 指定为API模式
            
            Thresholds thresholds = new Thresholds();
            thresholds.setQps(2); // 每秒最多2个请求
            request.setThresholds(thresholds);
            
            String dataId = strategyService.createStrategy(request);
            return ApiResponse.success("API流控规则创建成功,dataId: " + dataId);
        } catch (Exception e) {
            log.error("创建API流控规则失败", e);
            return ApiResponse.error(500, "创建API流控规则失败: " + e.getMessage());
        }
    }
 
    /**
     * 创建针对API分组的流控规则示例
     */
    @PostMapping("/createGroupRule")
    public ApiResponse<String> createGroupRule() {
        try {
            StrategyRequest request = new StrategyRequest();
            request.setStrategyName("API分组流控策略");
            request.setMonitoringType("FLOW_CONTROL");
            request.setControlMeasure("限流");
            request.setConfigCode("group-order-rule");
            request.setApiGroup("order_group"); // 分组标识
            request.setResourceMode("GROUP"); // 指定为分组模式
            
            Thresholds thresholds = new Thresholds();
            thresholds.setQps(3); // 每秒最多3个请求
            request.setThresholds(thresholds);
            
            String dataId = strategyService.createStrategy(request);
            return ApiResponse.success("API分组流控规则创建成功,dataId: " + dataId);
        } catch (Exception e) {
            log.error("创建API分组流控规则失败", e);
            return ApiResponse.error(500, "创建API分组流控规则失败: " + e.getMessage());
        }
    }
 
    /**
     * 创建针对路由的流控规则示例
     */
    @PostMapping("/createRouteRule")
    public ApiResponse<String> createRouteRule() {
        try {
            StrategyRequest request = new StrategyRequest();
            request.setStrategyName("路由级别流控策略");
            request.setMonitoringType("FLOW_CONTROL");
            request.setControlMeasure("限流");
            request.setConfigCode("route-business-rule");
            request.setBusinessType("business");
            request.setRoutePath("/api/**"); // 路由路径
            request.setResourceMode("ROUTE"); // 指定为路由模式
            
            Thresholds thresholds = new Thresholds();
            thresholds.setQps(5); // 每秒最多5个请求
            request.setThresholds(thresholds);
            
            String dataId = strategyService.createStrategy(request);
            return ApiResponse.success("路由级别流控规则创建成功,dataId: " + dataId);
        } catch (Exception e) {
            log.error("创建路由级别流控规则失败", e);
            return ApiResponse.error(500, "创建路由级别流控规则失败: " + e.getMessage());
        }
 
 
 
 
 
    }
}

2.8、创建api分组的实体类

package com.ruoyi.vehicle.DTO;
 
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
 
@Data
public class ApiGroupRequest {
    @NotBlank(message = "分组名称不能为空")
    private String groupName;
    
    @NotBlank(message = "描述不能为空")
    private String description;
    
    @NotNull(message = "排序不能为空")
    private Integer sort;
    
    private String businessType;
    
    private String parentCode; // 父级分组编码
    
    private Boolean enabled = true; // 是否启用
}

2.9、创建返回

package com.ruoyi.vehicle.DTO;
 
import lombok.Data;
import java.time.LocalDateTime;
 
@Data
public class ApiResponse<T> {
    private int code;
    private String message;
    private T data;
    private LocalDateTime timestamp = LocalDateTime.now();
    
    public static <T> ApiResponse<T> success(T data) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setCode(200);
        response.setMessage("成功");
        response.setData(data);
        return response;
    }
    
    public static <T> ApiResponse<T> error(int code, String message) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setCode(code);
        response.setMessage(message);
        return response;
    }
}

2.10、创建限流规则

package com.ruoyi.vehicle.DTO;
 
import lombok.AllArgsConstructor;
import lombok.Data;
 
@Data
public class FlowRuleDto {
    private String resource;
    private String limitApp;
    private Integer grade;
    private Double count;
    private Integer strategy;
    private Integer controlBehavior;
    private Boolean isClusterMode;
    private Integer intervalSec;
    private Integer warmUpPeriodSec;
    private Integer burstCount; // 可能不存在
    private Integer maxQueueingTimeMs; // 可能不存在
}

2.11、创建StrategyDetail

package com.ruoyi.vehicle.DTO;
 
import com.ruoyi.vehicle.Sentinel.GatewayFlowRule;
import lombok.Data;
 
import java.util.List;
import java.util.Date;
 
@Data
public class StrategyDetail {
    private String configCode;
    private List<GatewayFlowRule> rules;
    private Date updateTime;
}

2.12、创建策略的实体类

package com.ruoyi.vehicle.DTO;
 
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
 
@Data
public class StrategyRequest {
    @NotBlank(message = "策略名称不能为空")
    private String strategyName;
 
    private List<FlowRule> flowRules;
 
    @NotBlank(message = "监控类型不能为空")
    private String monitoringType;
    
    @NotNull(message = "阈值配置不能为空")
    private Thresholds thresholds;
    
    private String alertLevel = "INFO";
    
    private List<String> notifyUsers;
    
    @NotBlank(message = "控制措施不能为空")
    private String controlMeasure;
    
    @NotBlank(message = "配置编码不能为空")
    private String configCode;
    
    // 额外信息
    private String businessType;
    private String routePath = "/api/**";
    private String apiGroup;  // API分组标识
    private String description;
 
    
    
    // 添加按API路径进行流控的字段
    private String resourceMode = "ROUTE"; // 资源模式: ROUTE-路由, API-具体API路径, GROUP-分组
}

2.13、创建指定阈值实体类

package com.ruoyi.vehicle.DTO;
 
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.Max;
 
@Data
public class Thresholds {
    @Min(value = 1, message = "响应时长阈值必须大于0")
    private Integer responseTime = 10; // 秒
    
    @Min(value = 0, message = "错误率阈值不能小于0")
    @Max(value = 100, message = "错误率阈值不能大于100")
    private Integer errorRate = 20; // %
    
    @Min(value = 1, message = "QPS阈值必须大于0")
    private Integer qps = 100; // 次/秒
    
    @Min(value = 0, message = "队列阈值不能小于0")
    private Integer queueSize = 20;
    
    @Min(value = 0, message = "并发阈值不能小于0")
    private Integer concurrency = 20; // 次/秒
}

2.14、关于seninel

package com.ruoyi.vehicle.Sentinel;
 
import lombok.Data;
 
@Data
public class GatewayFlowRule {
    private String resource; // 资源名称
    private int count; // 阈值
    private int intervalSec = 1; // 统计时间窗口
    private int controlBehavior; // 流控效果
    private int maxQueueingTimeoutMs = 0; // 最大排队等待时长
    private int burstCount = 0; // 令牌桶容量
    private boolean clusterMode = false; // 是否集群模式
    private GatewayParamFlowItem paramItem; // 参数流控项
    
    // 静态方法:创建限流规则
    public static GatewayFlowRule createLimitRule(String resource, int qps) {
        GatewayFlowRule rule = new GatewayFlowRule();
        rule.setResource(resource);
        rule.setCount(qps);
        rule.setControlBehavior(0); // 0-快速失败, 1-匀速排队, 2-预热
        return rule;
    }
}
 
@Data
class GatewayParamFlowItem {
    private int parseStrategy; // 参数解析策略
    private String fieldName; // 参数名称
    private int threshold; // 阈值
    private int pattern; // 匹配模式
    private String matchStrategy; // 匹配策略
}
package com.ruoyi.vehicle.Sentinel;
 
import lombok.Data;
 
@Data
public class GatewayFlowRule {
    private String resource; // 资源名称
    private int count; // 阈值
    private int intervalSec = 1; // 统计时间窗口
    private int controlBehavior; // 流控效果
    private int maxQueueingTimeoutMs = 0; // 最大排队等待时长
    private int burstCount = 0; // 令牌桶容量
    private boolean clusterMode = false; // 是否集群模式
    private GatewayParamFlowItem paramItem; // 参数流控项
    
    // 静态方法:创建限流规则
    public static GatewayFlowRule createLimitRule(String resource, int qps) {
        GatewayFlowRule rule = new GatewayFlowRule();
        rule.setResource(resource);
        rule.setCount(qps);
        rule.setControlBehavior(0); // 0-快速失败, 1-匀速排队, 2-预热
        return rule;
    }
}
 
@Data
class GatewayParamFlowItem {
    private int parseStrategy; // 参数解析策略
    private String fieldName; // 参数名称
    private int threshold; // 阈值
    private int pattern; // 匹配模式
    private String matchStrategy; // 匹配策略
}

2.15、关于创建api分组

package com.ruoyi.vehicle.service;
 
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.ruoyi.vehicle.DTO.ApiGroupRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.util.HashMap;
import java.util.Map;
 
@Slf4j
@Service
public class ApiGroupService {
 
    private final ConfigService configService;
    private final NacosConfigProperties properties;
 
    @Autowired
    public ApiGroupService(ConfigService configService, NacosConfigProperties properties) {
        this.configService = configService;
        this.properties = properties;
    }
 
    /**
     * 创建API分组
     */
    public String createApiGroup(ApiGroupRequest request) throws NacosException {
        log.info("创建API分组: {}", request.getGroupName());
        
        // 1. 生成数据ID
        String dataId = generateDataId(request);
        
        // 2. 构建分组配置
        Map<String, Object> groupConfig = buildGroupConfig(request);
        
        // 3. 写入Nacos
        publishToNacos(dataId, groupConfig);
        
        log.info("API分组创建成功,Nacos配置ID: {}", dataId);
        return dataId;
    }
 
    /**
     * 生成Nacos配置的Data ID
     */
    private String generateDataId(ApiGroupRequest request) {
        return "api-groups-" + request.getGroupName();
    }
 
    /**
     * 构建API分组配置
     */
    private Map<String, Object> buildGroupConfig(ApiGroupRequest request) {
        Map<String, Object> config = new HashMap<>();
        config.put("groupName", request.getGroupName());
        config.put("description", request.getDescription());
        config.put("enabled", request.getEnabled());
        config.put("createTime", System.currentTimeMillis());
        return config;
    }
 
    /**
     * 发布配置到Nacos
     */
    private void publishToNacos(String dataId, Map<String, Object> config) throws NacosException {
        String content = JSON.toJSONString(config);
        boolean success = configService.publishConfig(
            dataId,
            properties.getGroup(),
            content
        );
        
        if (!success) {
            throw new RuntimeException("发布Nacos配置失败: " + dataId);
        }
        
        log.debug("Nacos配置发布成功 - DataId: {}, Content: {}", dataId, content);
    }
 
    /**
     * 获取配置内容
     */
    public String getConfigContent(String dataId) throws NacosException {
        return configService.getConfig(
            dataId,
            properties.getGroup(),
            properties.getTimeout()
        );
    }
}

2.16、关于json处理

package com.ruoyi.vehicle.service;
 
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
 
 
@Service
@Slf4j
public class JsonService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * JSON解析接口 - 需要流控
     */
    @SentinelResource(
        value = "parseJson",
        blockHandler = "parseJsonBlockHandler",
        fallback = "parseJsonFallback"
    )
    public Map<String, Object> parseJson(String jsonString) {
        log.info("Parsing JSON: {}", jsonString);
        
        // 模拟JSON解析
        try {
            Thread.sleep(150);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        ObjectMapper mapper = new ObjectMapper();
        try {
            return mapper.readValue(jsonString, Map.class);
        } catch (Exception e) {
            throw new RuntimeException("Invalid JSON format", e);
        }
    }
    
    /**
     * 批量JSON处理接口 - 需要流控
     */
    @SentinelResource(
        value = "batchParseJson",
        blockHandler = "batchParseJsonBlockHandler",
        fallback = "batchParseJsonFallback"
    )
    public List<Map<String, Object>> batchParseJson(List<String> jsonStrings) {
        log.info("Batch parsing JSON: {} items", jsonStrings.size());
        
        // 模拟批量处理
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        List<Map<String, Object>> results = new ArrayList<>();
        for (String jsonString : jsonStrings) {
            try {
                ObjectMapper mapper = new ObjectMapper();
                results.add(mapper.readValue(jsonString, Map.class));
            } catch (Exception e) {
                results.add(Collections.singletonMap("error", "Invalid JSON"));
            }
        }
        
        return results;
    }
    
    /**
     * 流控处理方法
     */
    public Map<String, Object> parseJsonBlockHandler(String jsonString, BlockException ex) {
        log.warn("Parse JSON blocked: {}, exception: {}", jsonString, ex.getClass().getSimpleName());
        return Collections.singletonMap("error", "flow_control");
    }
    
    public List<Map<String, Object>> batchParseJsonBlockHandler(List<String> jsonStrings, BlockException ex) {
        log.warn("Batch parse JSON blocked: {}, exception: {}", jsonStrings.size(), ex.getClass().getSimpleName());
        return Collections.singletonList(Collections.singletonMap("error", "flow_control"));
    }
    
    /**
     * 降级处理方法
     */
    public Map<String, Object> parseJsonFallback(String jsonString, Throwable throwable) {
        log.warn("Parse JSON fallback: {}", throwable.getMessage());
        return Collections.singletonMap("error", "fallback");
    }
    
    public List<Map<String, Object>> batchParseJsonFallback(List<String> jsonStrings, Throwable throwable) {
        log.warn("Batch parse JSON fallback: {}", throwable.getMessage());
        return Collections.singletonList(Collections.singletonMap("error", "fallback"));
    }
}

2.17、发布到nacos中格式转换

package com.ruoyi.vehicle.service;
 
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.exception.NacosException;
import com.ruoyi.vehicle.DTO.FlowRuleDto;
import com.ruoyi.vehicle.DTO.StrategyRequest;
import com.ruoyi.vehicle.DTO.Thresholds;
import com.ruoyi.vehicle.Sentinel.GatewayFlowRule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
 
import java.util.*;
import java.util.stream.Stream;
 
@Slf4j
@Service
public class StrategyService {
 
    private final ConfigService configService;
    private final NacosConfigProperties properties;
 
    @Autowired
    public StrategyService(ConfigService configService, NacosConfigProperties properties) {
        this.configService = configService;
        this.properties = properties;
    }
 
    // 在类级别添加版本检测
    private static final boolean IS_SENTINEL_2_X =
            Stream.of(FlowRule.class.getDeclaredMethods())
                    .anyMatch(m -> m.getName().equals("getBurstCount"));
 
    /**
     * 创建监控策略
     */
    public String createStrategy(StrategyRequest request) throws NacosException {
        log.info("创建监控策略: {}", request.getStrategyName());
        
        // 1. 生成数据ID
        String dataId = generateDataId(request);
        
        // 2. 构建规则
        String ruleJson = buildRuleJson(request);
        
        // 3. 写入Nacos
        publishToNacos(dataId, ruleJson);
        
        log.info("策略创建成功,Nacos配置ID: {}", dataId);
        return dataId;
    }
    /**
     * 将 FlowRule 对象转换为 Map 结构,便于 JSON 序列化
     */
    private Map<String, Object> buildFlowRuleMap(FlowRule rule) {
        Map<String, Object> ruleMap = new HashMap<>();
 
        // 基本必填字段
        ruleMap.put("resource", rule.getResource() != null ? rule.getResource() : "");
        ruleMap.put("limitApp", rule.getLimitApp() != null ? rule.getLimitApp() : "default");
        ruleMap.put("grade", rule.getGrade()); // 1: QPS, 0: 线程数
        ruleMap.put("count", rule.getCount()); // 阈值
        ruleMap.put("strategy", rule.getStrategy()); // 0: 直接, 1: 关联, 2: 链路
        ruleMap.put("controlBehavior", rule.getControlBehavior()); // 0: 快速失败, 1: Warm Up, 2: 排队等待
        ruleMap.put("clusterMode", rule.isClusterMode());
 
        // 可选字段 - 使用 try-catch 避免 NoSuchMethodError
        try {
            // Sentinel 1.8+ 版本
            ruleMap.put("maxQueueingTimeMs", rule.getMaxQueueingTimeMs());
        } catch (NoSuchMethodError | Exception e) {
            log.debug("无法获取 maxQueueingTimeMs 字段,使用默认值 500");
            ruleMap.put("maxQueueingTimeMs", 500);
        }
 
        try {
            // Sentinel 1.8+ 版本
            ruleMap.put("warmUpPeriodSec", rule.getWarmUpPeriodSec());
        } catch (NoSuchMethodError | Exception e) {
            log.debug("无法获取 warmUpPeriodSec 字段,使用默认值 30");
            ruleMap.put("warmUpPeriodSec", 30);
        }
 
 
        return ruleMap;
    }
 
 
    /**
     * 批量创建策略 - 支持在同一个配置文件中存储多个规则
     */
    public void createMultipleStrategies(List<StrategyRequest> requests) throws NacosException {
//        if (requests == null || requests.isEmpty()) {
//            log.warn("请求列表为空,无需处理");
//            return;
//        }
//
//        try {
//            StrategyRequest firstRequest = requests.get(0);
//            String baseDataId = generateDataId(firstRequest);
//
//            List<List<Map<String, Object>>> allRules = new ArrayList<>();
//
//            for (StrategyRequest request : requests) {
//                List<FlowRule> flowRules = request.getFlowRules();
//
//                if (flowRules == null || flowRules.isEmpty()) {
//                    log.warn("策略 {} 没有配置规则,跳过", request.getStrategyName());
//                    continue;
//                }
//
//                List<Map<String, Object>> ruleList = new ArrayList<>();
//                for (FlowRule rule : flowRules) {
//                    Map<String, Object> ruleMap = buildFlowRuleMap(rule);
//                    ruleList.add(ruleMap);
//                }
//
//                allRules.add(ruleList);
//            }
//
//            // 使用 PrettyFormat 格式化
//            String combinedRulesJson = JSON.toJSONString(allRules, SerializerFeature.PrettyFormat);
//
//            log.info("生成的 Nacos 配置内容:\n{}", combinedRulesJson);
//
//            // 发布到 Nacos
//            publishToNacos(baseDataId, combinedRulesJson);
//
//            log.info("批量策略创建成功,Nacos 配置 ID: {}", baseDataId);
//
//        } catch (Exception e) {
//            log.error("批量创建策略失败", e);
//            throw new RuntimeException("批量创建策略失败: " + e.getMessage(), e);
//        }
        if (requests == null || requests.isEmpty()) {
            log.warn("请求列表为空,无需处理");
            return;
        }
 
        try {
            StrategyRequest firstRequest = requests.get(0);
            String baseDataId = generateDataId(firstRequest);
 
            // 创建扁平化的规则列表
            List<Map<String, Object>> flatRules = new ArrayList<>();
            for (StrategyRequest request : requests) {
                List<FlowRule> flowRules = request.getFlowRules();
 
                if (flowRules == null || flowRules.isEmpty()) {
                    log.warn("策略 {} 没有配置规则,跳过", request.getStrategyName());
                    continue;
                }
 
                // 将每个规则直接添加到扁平化列表中
                for (FlowRule rule : flowRules) {
                    Map<String, Object> ruleMap = buildFlowRuleMap(rule);
                    flatRules.add(ruleMap);
                }
            }
 
            // 使用 PrettyFormat 格式化
            String combinedRulesJson = JSON.toJSONString(flatRules, SerializerFeature.PrettyFormat);
 
            log.info("生成的 Nacos 配置内容:\n{}", combinedRulesJson);
 
            // 发布到 Nacos
            publishToNacos(baseDataId, combinedRulesJson);
 
            log.info("批量策略创建成功,Nacos 配置 ID: {}", baseDataId);
 
        } catch (Exception e) {
            log.error("批量创建策略失败", e);
            throw new RuntimeException("批量创建策略失败: " + e.getMessage(), e);
        }
    }
 
    /**
     * 生成Nacos配置的Data ID
     */
    private String generateDataId(StrategyRequest request) {
        if (!StringUtils.hasText(request.getConfigCode())) {
            request.setConfigCode(UUID.randomUUID().toString().replace("-", "").substring(0, 12));
        }
        return "gateway-flow-rules-" + request.getConfigCode();
    }
 
    /**
     * 构建Sentinel规则JSON
     */
//    private String buildRuleJson(StrategyRequest request) {
//        // 创建一个包含所有必要字段的DTO
//        List<FlowRuleDto> ruleDtos = new ArrayList<>();
//        for (FlowRule rule : rules) {
//            FlowRuleDto dto = new FlowRuleDto();
//            dto.setResource(rule.getResource());
//            dto.setLimitApp(rule.getLimitApp());
//            dto.setGrade(rule.getGrade());
//            dto.setCount(rule.getCount());
//            dto.setStrategy(rule.getStrategy());
//            dto.setControlBehavior(rule.getControlBehavior());
//            dto.setClusterMode(rule.isClusterMode());
//            dto.setIntervalSec(rule.getIntervalSec());
//            dto.setWarmUpPeriodSec(rule.getWarmUpPeriodSec());
//
//            // 只有在存在时才设置
//            try {
//                dto.setBurstCount(rule.getBurstCount());
//            } catch (NoSuchMethodError e) {
//                // 忽略
//            }
//
//            ruleDtos.add(dto);
//        }
//
//        return JSON.toJSONString(ruleDtos);
//    }
 
 
    /**
     * 根据请求构建单个规则的JSON
     */
    private String buildRuleJson(StrategyRequest request) {
        List<FlowRule> rules = request.getFlowRules();
        if (rules == null || rules.isEmpty()) {
            return "[]";
        }
 
        // 创建规则列表
        List<Map<String, Object>> ruleList = new ArrayList<>();
        for (FlowRule rule : rules) {
            Map<String, Object> ruleMap = new HashMap<>();
            ruleMap.put("resource", rule.getResource());
            ruleMap.put("limitApp", rule.getLimitApp());
            ruleMap.put("grade", rule.getGrade());
            ruleMap.put("count", rule.getCount());
            ruleMap.put("strategy", rule.getStrategy());
            ruleMap.put("controlBehavior", rule.getControlBehavior());
            ruleMap.put("clusterMode", rule.isClusterMode());
            ruleMap.put("warmUpPeriodSec", rule.getWarmUpPeriodSec());
            ruleMap.put("maxQueueingTimeMs", rule.getMaxQueueingTimeMs());
 
            ruleList.add(ruleMap);
        }
 
        // 转换为JSON字符串
        return JSON.toJSONString(ruleList);
    }
 
    /**
     * 构建资源名称
     */
    private String buildResourceName(StrategyRequest request) {
        // 根据资源模式构建不同的资源名称
        switch (request.getResourceMode()) {
            case "API":
                // 针对具体API路径的流控
                return request.getRoutePath();
            case "GROUP":
                // 针对API分组的流控
                return "group@" + request.getApiGroup();
            case "ROUTE":
            default:
                // 默认基于路由的流控
                String businessType = StringUtils.hasText(request.getBusinessType()) ? 
                                     request.getBusinessType() : "default";
                return String.format("gateway_api::%s::%s", businessType, request.getRoutePath());
        }
    }
 
    /**
     * 获取控制行为类型
     */
    private int getControlBehavior(String measure) {
        if (measure == null) return 0;
        
        switch (measure) {
            case "熔断": return 0; // 快速失败
            case "排队": return 1; // 匀速排队
            case "禁止访问": return 2; // 直接拒绝
            case "限流": return 0; // 限流
            case "降级": return 0; // 降级
            default: return 0;
        }
    }
 
    /**
     * 发布配置到Nacos
     */
    private void publishToNacos(String dataId, String content) throws NacosException {
        try {
            boolean success = configService.publishConfig(
                    dataId,
                    properties.getGroup(),
                    content,
                    ConfigType.JSON.getType()
            );
 
            if (!success) {
                log.error("发布Nacos配置失败 - DataId: {}, Content: {}", dataId, content);
                throw new RuntimeException("发布Nacos配置失败:" + dataId);
            }
 
            log.debug("Nacos配置发布成功 - DataId: {}, Content: {}", dataId, content);
        } catch (NacosException e) {
            log.error("Nacos配置发布异常 - DataId: {}, Exception: {}", dataId, e.getMessage());
            throw new RuntimeException("发布Nacos配置失败:" + dataId + ",错误详情:" + e.getMessage(), e);
        }
    }
    
    /**
     * 获取配置内容
     */
    public String getConfigContent(String dataId) throws NacosException {
        return configService.getConfig(
            dataId,
            properties.getGroup(),
            properties.getTimeout()
        );
    }
}

如图所示:
3bcd2ecc01404167bf09e2acce1aacd4.png

启动类:

package com.ruoyi.vehicle;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigManagementApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ConfigManagementApplication.class, args);
        System.out.println("┌───────────────────────────────────────────────────────────────┐");
        System.out.println("│          配置管理服务启动成功!监听端口: 9001                    │");
        System.out.println("│          策略配置API: http://localhost:9001/config/strategies   │");
        System.out.println("└───────────────────────────────────────────────────────────────┘");
    }
}

application.yml:

server:
  port: 9001
  servlet:
    context-path: /config
 
spring:
  application:
    name: config-management-service
  redis:
    host: 你的ip
    port: 6379
    password: 
  config:
    import:
      - optional:nacos:config-management-service.yaml
  cloud:
    nacos:
      server-addr: 你的ip:8848
      config:
        group: DEFAULT_GROUP
        file-extension: yaml
        auto-refresh: true
        timeout: 3000
      discovery:
        enabled: true
        group: DEFAULT_GROUP
        weight: 1.0
        metadata:
          version: 1.0
    sentinel:
      transport:
        dashboard: http://你的ip:8858
        port: 8721
 
logging:
  level:
    com.ruoyi.vehicle: DEBUG
    com.alibaba.nacos: DEBUG  # 调整为 DEBUG 方便排查
 
management:
  endpoints:
    web:
      exposure:
        include: health,info  # 必须包含health
  endpoint:
    health:
      show-details: always

pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.ruoyi.vehicle</groupId>
        <artifactId>test-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
 
    <artifactId>json-service</artifactId>
 
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
        <spring-cloud.version>2021.0.4</spring-cloud.version>
    </properties>
 
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
 
    <dependencies>
        <!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <!-- Nacos Config Client -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.0.4.0</version> <!-- 与 Spring Boot 2.7.15 兼容 -->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
 
        <!-- Sentinel Core (for rule definitions) -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.6</version>
        </dependency>
 
        <!-- JSON Processing -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
 
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
 
        <!-- Validation -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
</project>

三、测试接口test-service

0450fa4467534a2fadf2d31c2c2c88c6.png

3.1、创建限流异常处理器

package com.ruoyi.vehicle.config;
 
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@Configuration
public class SentinelConfig {
 
    /**
     * 只定义自定义的BlockExceptionHandler
     * 不要定义sentinelWebMvcConfig Bean
     */
    @Bean
    public BlockExceptionHandler blockExceptionHandler() {
        return new CustomBlockExceptionHandler();
    }
 
    /**
     * 自定义限流异常处理器
     */
    public static class CustomBlockExceptionHandler implements BlockExceptionHandler {
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
            response.setStatus(429);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json;charset=UTF-8");
            String msg = "{\"status\":\"blocked\",\"resource\":\"" + e.getRule().getResource()
                    + "\",\"reason\":\"" + e.getClass().getSimpleName()
                    + "\",\"message\":\"流量控制触发,请求被限流\",\"timestamp\":" + System.currentTimeMillis() + "}";
            response.getWriter().print(msg);
        }
    }
}

3.2、测试接口controller

package com.ruoyi.vehicle.controller;
 
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.*;
 
import java.util.HashMap;
import java.util.Map;
 
@RestController
@RequestMapping("/api")
public class TestController {
 
    // 测试接口1 - 模拟业务接口
    @GetMapping("/business")
    @SentinelResource(value = "api/business", blockHandler = "handleBusinessBlock")
    public Map<String, Object> business() {
        Map<String, Object> result = new HashMap<>();
        result.put("status", "success");
        result.put("message", "Business API called successfully");
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
 
    // 测试接口2 - 模拟订单接口
    @GetMapping("/order")
    @SentinelResource(value = "api/order", blockHandler = "handleOrderBlock")
    public Map<String, Object> order() {
        Map<String, Object> result = new HashMap<>();
        result.put("status", "success");
        result.put("message", "Order API called successfully");
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
 
    // 测试接口3 - 模拟支付接口
    @GetMapping("/payment")
    @SentinelResource(value = "api/payment", blockHandler = "handlePaymentBlock")
    public Map<String, Object> payment() {
        Map<String, Object> result = new HashMap<>();
        result.put("status", "success");
        result.put("message", "Payment API called successfully");
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
 
    // 限流处理方法 - 业务接口
    public Map<String, Object> handleBusinessBlock(BlockException ex) {
        Map<String, Object> result = new HashMap<>();
        result.put("status", "blocked");
        result.put("message", "API business is blocked by Sentinel: " + ex.getClass().getSimpleName());
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
 
    // 限流处理方法 - 订单接口
    public Map<String, Object> handleOrderBlock(BlockException ex) {
        Map<String, Object> result = new HashMap<>();
        result.put("status", "blocked");
        result.put("message", "API order is blocked by Sentinel: " + ex.getClass().getSimpleName());
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
 
    // 限流处理方法 - 支付接口
    public Map<String, Object> handlePaymentBlock(BlockException ex) {
        Map<String, Object> result = new HashMap<>();
        result.put("status", "blocked");
        result.put("message", "API payment is blocked by Sentinel: " + ex.getClass().getSimpleName());
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
 
    // 健康检查接口
    @GetMapping("/health")
    public String health() {
        return "Service is running";
    }
 
    // 获取当前规则信息
    @GetMapping("/rules")
    public String getRules() {
        return "Current rules are loaded from Nacos: gateway-flow-rules-test004";
    }
}

3.3、启动类:

package com.ruoyi.vehicle;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class TextApplication {
    public static void main(String[] args) {
        SpringApplication.run(TextApplication.class, args);
    }
}

3.4、创建application.yml

server:
  port: 8082
  servlet:
    context-path: ""  # 关键!避免路径变长
spring:
  main:
    allow-circular-references: true
  application:
    name: test-service
  cloud:
    sentinel:
      transport:
        dashboard: 自己Ip:8858
        port: 8722
      eager: true  # 急切加载规则,启动时立即初始化
 
      datasource:
        ds1:
          nacos:
            server-addr: 自己ip:8848
            dataId: gateway-flow-rules-test004
            groupId: DEFAULT_GROUP
            rule-type: flow
            # 添加自动刷新配置
            auto-refresh: true
            data-type: json
      # 修复:启用过滤器
      filter:
        enabled: true  # 必须启用才能对HTTP请求进行限流
        url-patterns: /**  # 拦截所有URL
        order: -2147483648  # 设置高优先级
      # 添加Web上下文配置
      web-context-unify: false  # 防止链路被聚合
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always

3.5、pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.ruoyi</groupId>
    <artifactId>test-service</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
 
    <name>test-service</name>
    <description>Test service for Sentinel flow rules</description>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/>
    </parent>
 
    <properties>
        <java.version>17</java.version> <!-- 关键修复:必须与maven.compiler一致 -->
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
        <sentinel.version>1.8.5</sentinel.version>
        <!-- 关键:必须使用Spring Cloud Alibaba 2021.0.4.0配套的Sentinel版本 -->
        <spring-cloud.version>2021.0.4</spring-cloud.version>
    </properties>
 
    <dependencies>
 
 
        <!-- Alibaba Sentinel Core (必须与Spring Cloud Alibaba版本配套) -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
 
        <!-- Alibaba Sentinel Web MVC Adapter (关键!) -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-webmvc-adapter</artifactId>
            <version>1.8.5</version> <!-- 必须与您的 Sentinel 版本一致 -->
        </dependency>
 
 
        <!-- Spring Boot Starter AOP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
 
        <!-- Spring Boot Starter Validation -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
 
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
 
        <!-- Spring Boot Starter Actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
 
        <!-- Spring Boot Starter Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
 
 
 
 
 
 
 
        <!-- Alibaba Sentinel Datasource for Nacos -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
 
        <!-- Spring Cloud Alibaba Sentinel Starter -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2021.0.4.0</version>
        </dependency>
 
 
 
        <!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
 
        <!-- Alibaba Sentinel Datasource for Nacos -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
 
 
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
    <repositories>
        <repository>
            <id>aliyun</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
 
    <!-- 重要:统一依赖版本 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

四、调用openfegin调用进行限流

b189db57892d463f88238e453e740cb7.png

4.1、创建sentinel的处理类,使用CGLIB代理

package com.ruoyi.vehicle.config;
 
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
 
/**
 * Sentinel 配置类 - 关键修复
 * 1. 使用@Order确保最先加载
 * 2. 强制CGLIB代理
 * 3. 显式创建SentinelResourceAspect bean
 */
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) // 关键:强制CGLIB代理 + 暴露代理
@Order(0) // 确保最先加载
public class SentinelConfig {
 
    /**
     * 显式创建SentinelResourceAspect bean - 解决"Wrong state"问题的核心
     */
    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }
 
    /**
     * 添加初始化验证
     */
    @Bean
    public SentinelInitVerifier sentinelInitVerifier() {
        return new SentinelInitVerifier();
    }
 
    /**
     * 初始化验证器 - 检查Sentinel AOP是否正确初始化
     */
    public static class SentinelInitVerifier {
        public SentinelInitVerifier() {
            System.out.println("✅ [SentinelInitVerifier] SentinelResourceAspect 已注册到Spring容器");
            System.out.println("✅ [SentinelInitVerifier] CGLIB代理已启用: proxyTargetClass=true");
            System.out.println("✅ [SentinelInitVerifier] AOP切面初始化完成");
        }
    }
}

4.2、创建测试返回

package com.ruoyi.vehicle.config;
 
import com.ruoyi.vehicle.service.TestServiceFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
 
import java.util.HashMap;
import java.util.Map;
 
@Slf4j
@Component // 必须是 Spring Bean
public class TestServiceFeignClientFallback implements TestServiceFeignClient {
 
 
    @Override
    public Map<String, Object> business() {
        Map<String, Object> result = new HashMap<>();
        result.put("status", "fallback");
        result.put("message", "【降级】业务服务不可用");
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
 
    @Override
    public Map<String, Object> submitData() {
        Map<String, Object> result = new HashMap<>();
        result.put("status", "fallback");
        result.put("message", "【降级】数据提交服务不可用");
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
 
 
}

4.3、openfegin 测试controller

package com.ruoyi.vehicle.controller;
 
import com.ruoyi.vehicle.service.TestServiceFeignClient;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
 
import java.util.Map;
 
@RestController
@RequestMapping("/api/client")
@RequiredArgsConstructor
public class FeignClientController {
    
    private final TestServiceFeignClient testServiceFeignClient;
    
    // 3. 通过 Feign 调用 test-service
    @GetMapping("/business")
    public String callBusinessService() {
        System.out.println("🔵 收到请求:  /api/client/business");
        
        // 这里会触发 Sentinel 限流规则
        Map<String,Object> result = testServiceFeignClient.business();
        
        System.out.println("✅ Feign 调用成功: " + result);
        return "Feign Result: " + result;
    }
    
    @PostMapping("/submit")
    public String submitToBusiness(@RequestBody String data) {
        System.out.println("🔵 收到请求:  /api/client/submit | Data: " + data);
        
        Map<String,Object> result = testServiceFeignClient.submitData();
        return "Feign Submit Result: " + result;
    }
}

4.4、创建fegin客户端

package com.ruoyi.vehicle.service;
 
import com.ruoyi.vehicle.config.TestServiceFeignClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
 
import java.util.Map;
 
// 1. 定义 Feign 客户端
@FeignClient(
    name = "test-service",                // 目标服务名(必须与 test-service 的 spring.application.name 一致)
    path = "/api/business",               // 基础路径
    fallback = TestServiceFeignClientFallback.class // 降级实现类
)
public interface TestServiceFeignClient {
 
    @GetMapping("/business")
    Map<String, Object> business();
 
    @PostMapping("/submit")
    Map<String, Object> submitData();
}

4.5、启动类,开启openfegin

package com.ruoyi.vehicle;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
 
@SpringBootApplication
@EnableFeignClients // 启用 Feign 客户端
@EnableDiscoveryClient
@ServletComponentScan
@EnableAspectJAutoProxy(proxyTargetClass = true) // 双重保险,确保AOP生效
public class TestOpenFeignServiceApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(TestOpenFeignServiceApplication.class, args);
        System.out.println("✅ test-openfeign-service 启动成功!");
        System.out.println("✅ Feign 调用接口: http://localhost:8082/api/client/business");
        System.out.println("✅ Sentinel 资源名: GET:http://test-service/api/business");
 
 
        // 启动后验证Sentinel初始化
        System.out.println("✅ 应用启动成功,等待Sentinel初始化...");
        try {
            Thread.sleep(2000); // 等待AOP初始化
            System.out.println("✅ Sentinel应该已初始化完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4.6、application.yml

server:
  port: 9002  # test-openfeign-service 端口
 
spring:
  application:
    name: test-openfeign-service  # 调用方服务名
  cloud:
    nacos:
      discovery:
        server-addr: 自己ip:8848
      config:
        server-addr: 自己ip:8848
        file-extension: yaml
 
    sentinel:
      transport:
        dashboard: 自己ip:8858  # Sentinel Dashboard 地址
        port: 8719
      eager: true  # 急切加载规则
      datasource:
        # Feign 调用限流规则(关键配置!)
        feign-flow:
          nacos:
            server-addr: 自己ip:8848
            data-id: test-openfeign-service-feign-rules
            group-id: DEFAULT_GROUP
            rule-type: flow
            auto-refresh: true
            data-type: json
      # 启用 Feign 支持(必须!)
      feign:
        enabled: true
      web-context-unify: false  # 重要!防止 URL 路径被聚合
 
# Feign 超时配置(必须设置!)
feign:
  client:
    config:
      default:
        connectTimeout: 3000  # 连接超时3秒
        readTimeout: 5000    # 读取超时5秒
  # 启用 Sentinel 熔断
  sentinel:
    enabled: true
 
management:
  endpoints:
    web:
      exposure:
        include: "*"

4.7、bootstrap.yml

server:
  port: 9002
spring:
  application:
    name: test-openfeign-service
  cloud:
    nacos:
      discovery:
        server-addr: 自己Ip:8848
      config:
        server-addr: 自己ip:8848
        file-extension: yaml
        group: DEFAULT_GROUP

4.8、pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.ruoyi.vehicle</groupId>
        <artifactId>test-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
 
    <artifactId>test-openfeign-service</artifactId>
 
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
        <spring-cloud.version>2021.0.4</spring-cloud.version>
    </properties>
 
    <dependencyManagement>
        <dependencies>
            <!-- Spring Cloud Alibaba BOM -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
 
            <!-- Spring Cloud BOM -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
 
    <dependencies>
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
            <version>1.8.5</version> <!-- 与您项目中其他sentinel组件版本保持一致 -->
        </dependency>
 
        <!-- Sentinel Annotation AspectJ -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
            <version>1.8.5</version>
        </dependency>
 
        <!-- Spring Boot AOP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
 
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!-- 1. OpenFeign (无需版本号) -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
 
        <!-- 2. Sentinel 核心 (无需版本号) -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
 
        <!-- 3. Nacos 服务发现 (无需版本号) -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
 
        <!-- 4. Nacos 配置中心 (无需版本号) -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
 
        <!-- 5. Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
 
    <repositories>
        <repository>
            <id>aliyun</id>
            <url>http://maven.aliyun.com/repository/public</url>
        </repository>
        <repository>
            <id>central</id>
            <url>http://repo1.maven.org/maven2</url>
        </repository>
    </repositories>
 
</project>

自己创建测试demo,制作不易,如有用,请点个赞支持一下