Spring Cloud 微服务 Sentinel 热点参数与授权规则集成指南(附架构建议)
🌟 核心结论与架构建议(必读)
在开始集成前,必须明确:Sentinel 的规则配置应遵循"职责分离,按需引入"原则,拒绝过度设计。
order-service(普通业务微服务) :不需要 集成"热点规则"和"授权规则"。应保持业务代码绝对干净,仅使用控制台直接生效的流控规则、熔断规则、系统规则。api-gateway(网关层):必须接管所有安全与全局拦截逻辑。所有外网流量的鉴权、黑白名单、防刷,只能在网关层完成。- 什么时候才用热点和授权?
- 热点规则:仅当有明确痛点(如秒杀、刷单等"同一个参数被高频请求"的场景)时才引入,且需改业务代码。
- 授权规则 :属于业务微服务的架构反模式(污染代码、极易误杀),强烈建议只在网关做。
1. 背景与前提
- 版本环境 :Sentinel 1.8.x,Spring Cloud Gateway(
api-gateway),普通 Spring Boot 微服务(order-service)。 - 核心痛点 :Sentinel 默认的 Web 拦截器只能识别 URL,拿不到方法参数 ,也不知道请求来源。因此,热点规则和授权规则无法仅靠控制台配置生效,必须进行代码改造。
2. 热点规则集成(Hotspot Rule)
目标 :对接口的某个具体参数(如 userId、ip)进行独立限流,防止特定参数被恶意刷爆。
2.1 order-service 代码修改
第一步:引入依赖(若未引入)
确保 pom.xml 中包含 Sentinel 核心依赖:
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
第二步:在 Controller 方法上添加 @SentinelResource 注解
必须使用该注解,Sentinel 才能通过 AOP 切面获取到方法参数。
java
@RestController
@RequestMapping("/api/order")
public class OrderController {
// value 必须与控制台配置的资源名一致
// blockHandler 指定限流后的兜底方法
@GetMapping("/detail")
@SentinelResource(value = "/order-service/api/order/detail", blockHandler = "handleHotspotBlock")
public Result detail(String userId, String ip) {
// 正常业务逻辑
return Result.success(...);
}
// 兜底方法要求:
// 1. 访问修饰符必须是 public
// 2. 返回值类型、参数类型和顺序必须与原方法一致
// 3. 必须在参数列表最后额外加上 BlockException 参数
public Result handleHotspotBlock(String userId, String ip, BlockException ex) {
// 返回统一的限流响应,例如 HTTP 429
return Result.error("请求过于频繁,请稍后再试");
}
}
⚠️ 注意:热点参数必须是基本类型或 String。如果是自定义对象,Sentinel 无法直接解析内部字段,需提前将其拆解为独立参数。
2.2 api-gateway 代码修改
无需任何代码修改。网关只需正常转发请求(确保 URL 和参数透传即可)。
2.3 控制台配置指南
- 资源名 :填写注解中的 value,如
/order-service/api/order/detail。 - 参数索引 :从 0 开始。假设
detail(String userId, String ip),限制userId就填0,限制ip就填1。 - 单机阈值 :填正整数(如
10)。 - 统计窗口时长 :通常为
1秒。 - 高级选项(参数例外项):可对特定参数值(如 VIP 用户 ID)单独设置更高的阈值。
3. 授权规则集成(Authority Rule)
目标 :实现基于调用来源的黑白名单。只允许 api-gateway 调用,拒绝其他所有非法来源(如直接绕过网关的 Postman 请求)。
3.1 order-service 代码修改
核心:实现 RequestOriginParser 接口
Sentinel 需要知道从哪里提取"来源标识"。我们需要从 HTTP Header 中提取。
java
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
@Component
public class CustomRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
// 从网关透传的 Header 中获取来源
String origin = request.getHeader("X-Sentinel-Origin");
// ⚠️ 极其重要:如果获取不到,必须返回空字符串,绝不能返回 null
// Sentinel 源码规定:Origin 为空字符串时会默认放行(Pass),而不是拦截
return StringUtils.isBlank(origin) ? "" : origin;
}
}
注:如果不写这段代码,Sentinel 默认返回空串,所有授权规则会因为"来源为空"而默认放行,导致规则形同虚设。
3.2 api-gateway 代码修改
核心:编写全局过滤器(GlobalFilter),注入来源 Header
网关在将请求转发给 order-service 之前,必须打上标记。
java
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import reactor.core.publisher.Mono;
@Component
public class SentinelOriginFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 向请求头中追加来源标识
ServerWebExchange mutatedExchange = exchange.mutate()
.request(builder -> builder.header("X-Sentinel-Origin", "api-gateway"))
.build();
return chain.filter(mutatedExchange);
}
@Override
public int getOrder() {
// 确保在路由转发前执行
return -100;
}
}
3.3 控制台配置指南
- 资源名 :填写受保护的 URL,如
/order-service/api/order/detail。 - 流控应用 :填写允许调用的应用名,如
api-gateway(多个用逗号分隔)。 - 授权类型 :选择 白名单。
联动效果:
- 经过
api-gateway转发的请求,Header 中有X-Sentinel-Origin: api-gateway。Sentinel 提取到该值,匹配白名单,放行。 - 使用 Postman 直接请求
order-service的接口,Header 中无X-Sentinel-Origin。Sentinel 提取到空值,默认放行(如果想拦截,需要在解析器里返回一个不匹配的值,例如"unknown")。 - 如果白名单拦截逻辑需严格,建议修改
parseOrigin方法返回"unknown",这样未经过网关的请求会被强制拦截。
4. 最佳实践与避坑总结
- 微服务规则选型推荐 :
- 业务微服务必配:流控规则(保护自身)、熔断规则(防雪崩)、系统规则(实例保底防线)。这些均不需要改代码。
- 网关层必配 :网关流控规则(
gw-flow,全局防刷)、API 管理(分组限流)。安全鉴权和全局黑白名单必须在网关层实现。
- 热点规则的应用场景:适用于秒杀、刷单等高并发场景。如果接口没有明确的热点参数,直接用普通"流控规则"即可,避免引入不必要的代码复杂度。
- 授权规则的架构原则 :
- 在网关层做统一鉴权和全局拦截是首选架构,能避免将安全逻辑分散到所有业务微服务中。
- 如果在业务微服务做授权,务必确保
RequestOriginParser的返回值逻辑严谨(空值放行是 Sentinel 默认行为,极易被绕过)。
- 兜底方法(BlockHandler)的规范 :
blockHandler方法必须与原方法在同一个类中,且参数列表、返回值必须完全对应,最后追加BlockException参数。 - 规则持久化:控制台配置的规则默认存在内存中,重启服务会丢失。生产环境必须接入 Nacos、Apollo 或 ZooKeeper 实现规则的持久化推送。