
霸王餐CPS系统中Java实现接口限流的多种算法与落地技巧在“霸王餐”CPS系统中第三方回调、佣金查询、活动配置等接口常面临恶意刷量或突发流量冲击。若无有效限流机制将导致数据库压力激增、服务雪崩。本文结合计数器、滑动窗口、漏桶、令牌桶四种算法提供基于Guava、Redis及Sentinel的可落地限流方案。1. 固定窗口计数器简单但存在临界突刺适用于低精度场景如每日回调次数限制packagebaodanbao.com.cn.cps.ratelimit;importcom.google.common.cache.CacheBuilder;importcom.google.common.cache.CacheLoader;importcom.google.common.cache.LoadingCache;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicInteger;publicclassFixedWindowRateLimiter{privatefinalLoadingCacheString,AtomicIntegercounters;privatefinalintlimit;privatefinallongwindowSeconds;publicFixedWindowRateLimiter(intlimit,longwindowSeconds){this.limitlimit;this.windowSecondswindowSeconds;this.countersCacheBuilder.newBuilder().expireAfterWrite(windowSeconds,TimeUnit.SECONDS).build(newCacheLoader(){OverridepublicAtomicIntegerload(Stringkey){returnnewAtomicInteger(0);}});}publicbooleantryAcquire(Stringkey){AtomicIntegercountercounters.getUnchecked(key);intcurrentcounter.incrementAndGet();returncurrentlimit;}}使用示例FixedWindowRateLimiterlimiternewFixedWindowRateLimiter(100,60);// 每分钟100次if(!limiter.tryAcquire(callback:merchantId)){thrownewRuntimeException(Too many requests);}2. 滑动日志窗口高精度但内存开销大记录每次请求时间戳动态计算窗口内请求数publicclassSlidingLogRateLimiter{privatefinalLoadingCacheString,ListLongrequestLogs;privatefinalintlimit;privatefinallongwindowMillis;publicSlidingLogRateLimiter(intlimit,longwindowSeconds){this.limitlimit;this.windowMilliswindowSeconds*1000;this.requestLogsCacheBuilder.newBuilder().expireAfterAccess(windowSeconds,TimeUnit.SECONDS).build(newCacheLoader(){OverridepublicListLongload(Stringkey){returnnewArrayList();}});}publicsynchronizedbooleantryAcquire(Stringkey){longnowSystem.currentTimeMillis();ListLonglogsrequestLogs.getUnchecked(key);// 清除窗口外记录logs.removeIf(ts-now-tswindowMillis);if(logs.size()limit){returnfalse;}logs.add(now);returntrue;}}3. 令牌桶算法支持突发流量使用GuavaRateLimiter实现单机限流ComponentpublicclassTokenBucketRateLimiter{privatefinalMapString,com.google.common.util.concurrent.RateLimiterlimitersnewConcurrentHashMap();publicRateLimitergetOrCreate(Stringkey,doublepermitsPerSecond){returnlimiters.computeIfAbsent(key,k-com.google.common.util.concurrent.RateLimiter.create(permitsPerSecond));}publicbooleantryAcquire(Stringkey,doublepermitsPerSecond){returngetOrCreate(key,permitsPerSecond).tryAcquire();}}// Controller中使用AutowiredprivateTokenBucketRateLimitertokenLimiter;PostMapping(/commission/query)publicObjectqueryCommission(RequestBodyQueryDTOdto){if(!tokenLimiter.tryAcquire(query:dto.getUserId(),10.0)){thrownewRuntimeException(Request too fast);}// 业务逻辑}4. RedisLua实现分布式令牌桶适用于集群环境保证全局一致性ComponentpublicclassDistributedTokenBucket{privatefinalStringRedisTemplateredisTemplate;publicDistributedTokenBucket(StringRedisTemplateredisTemplate){this.redisTemplateredisTemplate;}privatestaticfinalStringLUA_SCRIPTlocal key KEYS[1]\nlocal rate tonumber(ARGV[1])\nlocal capacity tonumber(ARGV[2])\nlocal now tonumber(ARGV[3])\nlocal requested tonumber(ARGV[4])\nlocal tokens redis.call(GET, key)\nif tokens false then\n tokens capacity\nelse\n local last_time redis.call(GET, key .. :ts)\n if last_time false then last_time now end\n tokens math.min(capacity, tonumber(tokens) (now - tonumber(last_time)) * rate)\nend\nif tokens requested then\n redis.call(SET, key, tokens - requested)\n redis.call(SET, key .. :ts, now)\n return 1\nelse\n return 0\nend;publicbooleantryAcquire(Stringkey,doublerate,intcapacity,intpermits){DefaultRedisScriptLongscriptnewDefaultRedisScript(LUA_SCRIPT,Long.class);LongresultredisTemplate.execute(script,Collections.singletonList(tb:key),String.valueOf(rate),String.valueOf(capacity),String.valueOf(System.currentTimeMillis()),String.valueOf(permits));returnresult!nullresult1;}}调用示例booleanalloweddistributedTokenBucket.tryAcquire(callback:meituan,50.0,// 每秒50个令牌100,// 桶容量1001// 每次请求1个令牌);if(!allowed)thrownewRuntimeException(Rate limited);5. Sentinel集成生产级方案通过注解实现细粒度限流PostConstructpublicvoidinitRules(){ListFlowRulerulesnewArrayList();FlowRulerulenewFlowRule(commission_callback_api).setGrade(RuleConstant.FLOW_GRADE_QPS).setCount(200)// 单机QPS上限200.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)// 匀速排队.setMaxQueueingTimeMs(500);rules.add(rule);FlowRuleManager.loadRules(rules);}SentinelResource(valuecommission_callback_api,blockHandlerhandleBlocked)PostMapping(/callback)publicResponseEntity?handleCallback(RequestBodyCallbackDTOdto){returnbaodanbao.com.cn.cps.service.CallbackService.process(dto);}publicResponseEntity?handleBlocked(BlockExceptionex){returnResponseEntity.status(429).body(Too many requests);}本文著作权归 俱美开放平台 转载请注明出处