Sentinel 官方限流与幂等融合企业级完整方案
Sentinel 官方限流与幂等融合企业级完整方案
一、前言
前面两篇文章我们分别实现了Redis+AOP 幂等防重、自定义 Redis 限流 + 幂等双层防护。
但在微服务、分布式生产环境中,自研 Redis 限流存在明显短板:
- 无可视化控制台,规则改代码必须重启服务;
- 缺少熔断、降级、热点参数限流、系统自适应保护;
- 无法全链路流量观测、实时监控、动态流控规则。
阿里 Sentinel 是微服务生态标准流量防护组件,内置本地限流、热点限流、熔断降级、线程隔离、系统保护,支持 Nacos 动态推送规则。
本文实现Sentinel 官方限流框架 + Redis Token 幂等深度融合,形成企业标准三层防护链路:
Sentinel 限流熔断 → AOP 幂等校验 → 业务逻辑
- Sentinel:保护服务,拦截洪峰、恶意刷、下游故障雪崩
- Redis 幂等 Token:保护数据,拦截重复提交、网关 / 消息重试
二、融合架构设计
2.1 执行顺序(绝对不能颠倒)
- 请求进入接口,先执行
@SentinelResource拦截,触发 Sentinel 本地限流、热点限流、熔断降级;流量超限直接走 blockHandler 快速返回,不进入后续逻辑; - 流量校验通过后,进入自定义
@IdempotentAOP 切面,校验一次性幂等 Token;Token 不存在直接拦截; - 两层校验全部放行,执行业务代码;
- 业务异常场景:主动清理 Redis 幂等 Token,允许用户正常重试。
2.2 分层防护体系
| 层级 | 能力 | 技术实现 | 解决问题 |
|---|---|---|---|
| 流量防护层 | 限流、熔断、降级、热点参数控流 | Sentinel 官方框架 | 高并发打垮服务、下游雪崩、单用户疯狂刷接口 |
| 请求防重层 | 一次性令牌防重复提交 | Redis+Lua 原子校验 + AOP | 重复下单、重复扣款、MQ 重复消费 |
| 数据兜底层 | 数据库唯一索引 / 乐观锁 | MySQL 约束 | 极端并发下最终数据一致性兜底 |
2.3 技术依赖版本
<!-- SpringBoot Redis 幂等依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Sentinel 核心依赖 SpringCloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2022.0.0.0</version>
</dependency>
<!-- Sentinel控制台传输依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
三、前置复用基础代码(幂等模块,与前文通用)
3.1 幂等注解 @Idempotent
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Idempotent {
// 重复提交提示
String message() default "请勿重复提交操作";
// Token过期时间 单位秒
long expire() default 600;
}
3.2 Redis 幂等工具类(Lua 原子校验,解决查询删除非原子并发漏洞)
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
@Component
public class IdempotentUtil {
@Resource
private RedisTemplate<String, Object> redisTemplate;
private static final String TOKEN_KEY_PREFIX = "idempotent:token:";
// Lua脚本:get+del原子执行,避免并发穿透
private static final String CHECK_DEL_SCRIPT = """
local tokenKey = KEYS[1]
local exist = redis.call('GET', tokenKey)
if exist then
redis.call('DEL', tokenKey)
return 1
end
return 0
""";
/** 生成幂等Token */
public String generateToken(long expireSecond) {
String token = java.util.UUID.randomUUID().toString().replace("-", "");
String key = TOKEN_KEY_PREFIX + token;
redisTemplate.opsForValue().set(key, System.currentTimeMillis(), expireSecond, java.util.concurrent.TimeUnit.SECONDS);
return token;
}
/** 校验Token,存在则删除返回true,不存在返回false */
public boolean validToken(String token) {
if (!StringUtils.hasText(token)) return false;
String key = TOKEN_KEY_PREFIX + token;
DefaultRedisScript<Long> script = new DefaultRedisScript<>(CHECK_DEL_SCRIPT, Long.class);
Long result = redisTemplate.execute(script, Collections.singletonList(key));
return Long.valueOf(1).equals(result);
}
/** 业务异常时主动清除Token,支持重试 */
public void clearToken(String token) {
if (StringUtils.hasText(token)) {
redisTemplate.delete(TOKEN_KEY_PREFIX + token);
}
}
}
3.3 幂等 AOP 切面(Sentinel 放行后才执行)
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
public class IdempotentAspect {
@Resource
private IdempotentUtil idempotentUtil;
private static final String TOKEN_HEADER = "idempotent-token";
@Around("@annotation(idempotent)")
public Object around(ProceedingJoinPoint point, Idempotent idempotent) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader(TOKEN_HEADER);
boolean pass = idempotentUtil.validToken(token);
if (!pass) {
return Result.fail(idempotent.message());
}
try {
// 执行业务
return point.proceed();
} catch (Exception e) {
// 业务异常,清理token,允许用户重新提交
idempotentUtil.clearToken(token);
throw e;
}
}
}
3.4 统一返回体 Result
public class Result<T> {
private Integer code;
private String msg;
private T data;
public static <T> Result<T> fail(String msg) {
Result<T> r = new Result<>();
r.setCode(500);
r.setMsg(msg);
return r;
}
public static <T> Result<T> success(T data) {
Result<T> r = new Result<>();
r.setCode(200);
r.setMsg("操作成功");
r.setData(data);
return r;
}
// getter/setter省略
}
四、Sentinel 限流融合核心实现
4.1 Sentinel 资源注解规范
使用@SentinelResource标记接口资源,配置:
- value:资源名(控制台配置限流规则的唯一标识)
- blockHandler:限流 / 熔断兜底方法
- fallback:业务异常降级方法
4.2 Controller 完整示例(Sentinel + 幂等双注解共存)
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping("/api/order")
public class OrderController {
@Resource
private IdempotentUtil idempotentUtil;
// 获取幂等Token接口(无需限流,轻量接口)
@GetMapping("/getToken")
public Result<String> getToken() {
String token = idempotentUtil.generateToken(600);
return Result.success(token);
}
/**
* 下单核心接口:Sentinel限流 + 幂等双重防护
* 1. @SentinelResource:Sentinel拦截QPS、热点参数、熔断
* 2. @Idempotent:拦截重复提交
*/
@PostMapping("/submit")
@SentinelResource(value = "submitOrder", blockHandler = "blockHandler", fallback = "fallback")
@Idempotent(message = "订单请勿重复提交", expire = 600)
public Result<String> submitOrder() {
// 模拟下单业务逻辑
return Result.success("订单创建成功");
}
// Sentinel限流/熔断兜底方法(参数必须和原方法一致,最后追加BlockException)
public Result<String> blockHandler(BlockException ex) {
return Result.fail("当前访问人数过多,请稍后重试");
}
// 业务异常降级兜底
public Result<String> fallback() {
return Result.fail("服务暂时繁忙,请稍后再提交");
}
}
五、Sentinel 控制台配置规则(生产标准配置)
5.1 流控规则(普通 QPS 限流)
资源名:submitOrder
阈值类型:QPS
单机阈值:5(每秒最多 5 次请求)
流控效果:快速失败
5.2 热点参数限流(精细化单用户防刷,推荐)
如果接口入参携带userId,配置热点规则:
参数索引:0(userId 参数下标)
单机阈值:3(单个用户每秒最多 3 次下单)
限流时长:2 秒
解决同一用户恶意刷下单接口问题。
5.3 熔断降级规则(防止下游服务雪崩)
统计时长:10 秒
熔断触发阈值:异常比例 50%
熔断时长:3 秒
下游支付、库存服务大量报错时,自动切断调用,保护上游。
六、完整调用链路演示
-
前端页面加载,调用
/api/order/getToken获取一次性 Token; -
提交下单请求,Header 携带
idempotent-token:xxxx; -
第一层拦截:Sentinel
@SentinelResource- QPS 超限 / 热点参数超限:执行 blockHandler 返回繁忙,流程终止;
- 流量正常,放行进入下一层;
-
第二层拦截:
@IdempotentAOP 切面
- Token 不存在:返回重复提交,流程终止;
- Token 校验通过并删除,执行业务;
-
业务正常执行:返回成功;
-
业务抛出异常:自动清除 Redis Token,用户可重新提交。
七、融合方案核心优势对比自研 Redis 限流
7.1 Sentinel 优势
- 动态规则:控制台修改限流阈值、熔断时长,无需重启服务;
- 多维度防护:QPS 限流、热点参数、线程隔离、系统自适应保护;
- 实时监控:QPS、阻塞数、异常率可视化面板;
- 集群流控:结合 Nacos 实现分布式全局限流;
- 完善降级策略:异常降级、超时降级、返回静态兜底数据。
7.2 幂等 Token 优势
- 解决 Sentinel 无法处理的重复提交、网络重试、MQ 重复消费;
- Redis 分布式全局防重,集群多实例统一生效;
- Lua 原子操作,高并发无穿透漏洞。
7.3 二者互补,无功能重叠
- Sentinel 管流量多少,防服务崩溃;
- 幂等 Token 管请求是否重复,防脏数据;
八、生产环境进阶优化方案
8.1 网关层前置 Sentinel 防护(推荐分层)
Spring Cloud Gateway 整合 Sentinel,在网关提前拦截超限流量,避免无效请求转发至业务服务,减少资源损耗。
8.2 幂等 Token 过期时间差异化
- 支付、下单接口:10 分钟过期
- 表单普通提交:5 分钟过期
- 秒杀短时效操作:1 分钟过期
8.3 Sentinel 与分布式锁组合高并发交易
秒杀、库存扣减场景:
Sentinel 热点限流 → 幂等 Token 防重复下单 → Redisson 分布式锁防超卖 → 数据库最终兜底。
8.4 Redis 故障降级兼容
增加 Redis 健康检测,Redis 不可用时临时关闭幂等校验,仅保留 Sentinel 流量防护,保证服务不宕机。
8.5 统一全局异常处理整合 Sentinel
全局异常捕获BlockException、业务异常,统一返回标准化 JSON,避免页面出现原生异常堆栈。
九、高频生产问题答疑
Q1:为什么不用 Sentinel 做重复请求防重?
A:Sentinel 仅做流量频率控制,只限制单位时间访问次数,无法识别完全相同的重复提交请求。用户一秒点击 3 次,Sentinel 允许 3 次流量,但业务上只允许 1 次下单,必须依赖 Token 幂等。
Q2:Sentinel 本地限流是否存在集群流量不均?
A:单机 Sentinel 仅本地计数,多实例集群总流量会放大;高并发分布式场景可开启 Sentinel 集群流控,结合 Nacos 统一计数。
Q3:幂等 Token 删除失败会不会导致永久无法提交?
A:设置固定过期时间兜底,即使代码异常未删除,超时后 Token 自动失效,不会永久拦截用户。
Q4:Sentinel blockHandler 与 AOP 执行顺序谁先谁后?
A:@SentinelResource基于拦截器实现,优先级高于 Spring AOP;必然先限流校验,再进入幂等切面,符合防护逻辑。
十、总结
- Sentinel 限流 + Redis Token 幂等是微服务项目标准企业级流量 + 数据双重防护方案;
- 执行顺序固定:Sentinel 流量拦截在前,幂等防重在后;
- Sentinel 解决系统稳定性,幂等解决数据一致性,二者缺一不可;
- 代码完全兼容前两篇文章的幂等模块,无缝接入现有项目,控制台可视化运维,适配生产集群、秒杀、支付、表单提交全场景。
更多推荐



所有评论(0)