Java接口限流核心是控制单位时间请求量,常用Guava RateLimiter(单机令牌桶)或Redis+Lua(分布式滑动窗口),结合Spring AOP实现声明式限流,并需关注可观测性、动态配置与降级策略。

Java中实现接口访问限流,核心是控制单位时间内请求的通过数量,防止系统被突发流量压垮。常用方式不是靠“拦截所有请求再判断”,而是选用轻量、线程安全、低延迟的限流算法,并结合实际场景选择合适实现方式——比如用Guava RateLimiter做单机简单限流,或用Redis+Lua做分布式精确限流。
基于Guava RateLimiter的单机令牌桶限流
适合Spring Boot单体应用、内部服务或QPS不高(如≤1000)的接口。RateLimiter基于令牌桶算法,平滑突发流量,使用简单且无外部依赖。
- 引入依赖:com.google.guava:guava
- 定义限流器实例(建议用static final或Spring Bean管理):
RateLimiter limiter = RateLimiter.create(100.0); // 每秒100个令牌 - 在接口方法中尝试获取令牌:
if (!limiter.tryAcquire()) { throw new RuntimeException("请求过于频繁"); }
注意:tryAcquire()默认不阻塞,超时立即返回false;也可传入超时时间(如tryAcquire(1, TimeUnit.MILLISECONDS))做更精细控制。 - 不建议在高频循环中反复创建RateLimiter,它本身有预热和动态调整机制,复用更稳定。
基于Redis + Lua脚本的分布式滑动窗口限流
适用于微服务或多节点部署,需保证全集群共享计数。滑动窗口比固定窗口更精准(避免窗口切换时的流量尖峰),而Lua脚本能保证“读-改-写”原子性,避免竞态。
- Key设计示例:rate_limit:{interface_name}:{user_id}(按接口+用户维度限流)
- Lua脚本要点:
– 使用redis.call("ZREMRANGEBYSCORE", key, 0, now - windowMs)清理过期时间戳
– 用redis.call("ZCARD", key)获取当前请求数
– 若未超限,执行redis.call("ZADD", key, now, request_id)并设置过期
– 返回当前数量与阈值比较结果 - Java调用示例(使用Lettuce或Jedis):
封装成工具方法,传入key、窗口毫秒数、最大请求数,返回是否允许通过。
结合Spring AOP与自定义注解实现声明式限流
提升可维护性,避免业务代码侵入限流逻辑。可统一处理限流拒绝响应(如返回429 Too Many Requests)。
立即学习“Java免费学习笔记(深入)”;
- 定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit { int value() default 100; long periodMs() default 1000; } - 编写切面:
解析注解参数 → 构造唯一限流key(如类名+方法名+参数摘要)→ 调用底层限流器(Guava或Redis)→ 拒绝时抛出异常或返回ResponseEntity.status(429).build() - 在Controller方法上直接使用:
@RateLimit(value = 50, periodMs = 60_000)
public String getData() { ... }
生产环境关键注意事项
限流不是加了就完事,必须考虑可观测性、降级策略和配置灵活性。










