要实现“两次请求的时间间隔必须在3秒以上”这种需求,需要根据具体的业务场景选择合适的Sentinel配置方式。本文博主将介绍几种实现方案:
方案1:使用排队等待流控效果(最接近需求)
# Sentinel规则配置(对应控制台设置)规则类型:流控规则 资源名:yourResource 流控模式:直接 阈值类型:QPS 阈值:0.33(因为1/3≈0.33,即每秒最多0.33个请求) 流控效果:排队等待 超时时间:3000ms(3秒)控制台设置:
┌─────────────────────────────────────┐ │ 添加流控规则 │ ├─────────────────────────────────────┤ │ 资源名: yourResource │ │ 阈值类型: QPS │ │ 单机阈值: 0.33 │ │ 流控模式: [直接] │ │ │ │ 流控效果: [排队等待] │ │ 超时时间: 3000 ms │ └─────────────────────────────────────┘工作原理:
- 请求进入队列排队
- 每个请求处理间隔至少3秒
- 如果请求到达太快,会在队列中等待
- 如果等待时间超过3秒,会被拒绝
代码示例:
@RestControllerpublicclassMyController{@GetMapping("/api/resource")@SentinelResource(value="myResource",blockHandler="handleBlock")publicStringmyApi(){// 业务逻辑return"success";}// 阻塞处理函数publicStringhandleBlock(BlockExceptione){return"请求太频繁,请至少间隔3秒再试";}}方案2:使用Warm Up预热模式(接近但不完全符合)
规则类型:流控规则 资源名:yourResource 阈值类型:QPS 阈值:0.33 流控效果:Warm Up 预热时长:3秒方案3:使用热点参数限流(针对同一用户/设备)
如果需要针对同一用户限制3秒间隔,使用热点参数规则:
@GetMapping("/api/resource")@SentinelResource(value="myResource")publicStringmyApi(@RequestParamStringuserId){// 业务逻辑return"success";}控制台配置:
规则类型:热点参数规则 资源名:myResource 参数索引:0(对应userId) 单机阈值:1(每个userId) 统计窗口时长:3000ms(3秒)方案4:自定义实现(最灵活)
@ComponentpublicclassRequestIntervalLimiter{privatefinalMap<String,Long>lastRequestTime=newConcurrentHashMap<>();publicbooleanallowRequest(Stringkey){longcurrentTime=System.currentTimeMillis();LonglastTime=lastRequestTime.get(key);if(lastTime==null){lastRequestTime.put(key,currentTime);returntrue;}// 检查时间间隔if(currentTime-lastTime>=3000){lastRequestTime.put(key,currentTime);returntrue;}returnfalse;}}@RestControllerpublicclassMyController{@AutowiredprivateRequestIntervalLimiterlimiter;@GetMapping("/api/resource")publicResponseEntity<?>myApi(HttpServletRequestrequest){StringclientId=getClientId(request);// 获取客户端标识if(!limiter.allowRequest(clientId)){returnResponseEntity.status(429).body("请求太频繁,请至少间隔3秒再试");}// 业务逻辑returnResponseEntity.ok("success");}privateStringgetClientId(HttpServletRequestrequest){// 根据业务获取客户端标识// 可以是:userId, IP, sessionId等returnrequest.getHeader("X-User-Id")!=null?request.getHeader("X-User-Id"):request.getRemoteAddr();}}方案5:使用Sentinel的系统自适应保护(全局限制)
spring:cloud:sentinel:filter:enabled:true# 系统规则 - 限制整体QPSdatasource:ds1:nacos:server-addr:localhost:8848dataId:sentinel-system-rulesrule-type:system系统规则配置:
{"avgRt":1000,"highestSystemLoad":0.8,"qps":0.33,// 全局QPS限制"highestCpuUsage":0.8,"maxThread":50}推荐方案比较
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 排队等待 | Sentinel原生支持 | 不是严格3秒间隔 | 需要均匀处理请求 |
| 自定义实现 | 最精确控制 | 需要自己实现 | 精确的3秒间隔需求 |
| 热点参数 | 按用户/设备限制 | 配置复杂 | 针对特定用户限制 |
| 系统规则 | 全局控制 | 不够精确 | 系统整体保护 |
最接近需求的配置
如果严格要求"两次请求间隔必须≥3秒":
使用方案1的排队等待:
QPS阈值 = 1 / 3 ≈ 0.33 超时时间 = 3000ms配合自定义逻辑增强:
@ComponentpublicclassStrictIntervalLimiter{privatefinalLoadingCache<String,Long>requestCache=CacheBuilder.newBuilder().expireAfterWrite(3,TimeUnit.SECONDS).build(newCacheLoader<String,Long>(){@OverridepublicLongload(Stringkey){returnSystem.currentTimeMillis();}});publicbooleantryAcquire(Stringkey){try{// 如果key存在,说明3秒内有过请求LonglastTime=requestCache.getIfPresent(key);if(lastTime!=null){returnfalse;}requestCache.put(key,System.currentTimeMillis());returntrue;}catch(Exceptione){returnfalse;}}}最佳实践建议:
- 如果只是需要大致3秒间隔,使用方案1(排队等待)
- 如果需要严格精确的3秒间隔,使用方案4(自定义实现)
- 如果需要针对不同用户分别限制,使用方案3(热点参数)+ 方案4组合