限流网关的核心目标不是替代 Sentinel 或 Resilience4j,而是作为轻量级 API 入口层实现单机限流,依托 AtomicInteger、Semaphore、Guava RateLimiter 等并发工具封装计数器、并发数、令牌桶策略,并支持动态规则与多框架集成。

限流网关的核心目标不是替代 Sentinel 或 Resilience4j
微服务架构中,限流网关通常位于 API 入口(如 Spring Cloud Gateway 或自研网关层),它的轻量级体现在不依赖外部组件、不引入复杂配置、不侵入业务逻辑。Java 并发工具类(如 AtomicInteger、Semaphore、RateLimiter(Guava)、ConcurrentHashMap + ScheduledExecutorService)适合封装单机维度的限流策略,适用于中小流量场景或作为兜底/降级层。
用 AtomicInteger 实现简单计数器限流(QPS 控制)
适用于固定时间窗口内限制总请求数,实现简单、无锁开销低,但存在“边界突刺”问题(如 0:59.9 和 1:00.1 可能各放行 100 次)。
- 维护一个原子整数 count 和上一窗口开始时间 lastResetTime
- 每次请求检查是否跨窗口:若当前时间 - lastResetTime ≥ 窗口时长(如 1000ms),则重置 count = 0,lastResetTime = 当前时间
- 若 count.incrementAndGet() ≤ 阈值,则放行;否则拒绝
- 注意:需配合 volatile long 或 AtomicLong 存储 lastResetTime,避免可见性问题
用 Semaphore 实现并发数限流(连接数/线程数控制)
适合保护下游资源不被突发并发压垮(如数据库连接池、HTTP 客户端连接),本质是控制同时执行的请求数,而非单位时间请求数。
- 初始化时指定 permits 数量(如 new Semaphore(50))
- 网关拦截器中 tryAcquire(),成功则继续链路,失败返回 429
- 务必在 filter chain 结束后(无论成功失败)调用 release(),建议用 try-finally 包裹
- 可搭配 Fairness 参数控制是否公平调度,高吞吐场景建议 false(默认)
用 Guava RateLimiter 做平滑令牌桶限流(推荐用于多数场景)
相比计数器更平滑,支持预热、支持突发流量缓冲,且线程安全、无状态,适合作为 Filter 或 GlobalFilter 的核心组件。
立即学习“Java免费学习笔记(深入)”;
- 按路由粒度或服务名构建 RateLimiter 实例,缓存到 ConcurrentHashMap
- 使用 create(double permitsPerSecond) 或 create(double permitsPerSecond, long warmupPeriod, TimeUnit) 预热
- 拦截时调用 limiter.tryAcquire() —— 返回 true 放行,false 拒绝;或用 acquire() 阻塞等待(不推荐网关层阻塞)
- 注意:RateLimiter 是单实例、线程安全的,不要每次 new,也不要按请求 new
结合路由元数据动态加载限流规则
真正“轻量”不等于“写死阈值”。可通过配置中心(如 Nacos、Apollo)监听变更,或从请求头/X-Route-ID 解析路由标识,再查本地缓存获取对应限流参数。
- 定义 RuleKey = serviceId + "-" + routeId,作为限流器 map 的 key
- 监听配置变更时,对新增 key 初始化 RateLimiter,对变更 key 替换旧实例(注意旧实例不再使用即可,无需显式销毁)
- 未匹配到规则时走默认限流器(如 100 QPS),避免空指针或全放行
- 可加一层装饰器:记录被限流次数 + 时间戳,供 Prometheus 暴露 /actuator/metrics/ratelimit.rejected
不依赖 Spring Cloud Gateway 也能嵌入 WebMvc 或 WebFlux
如果是传统 Spring Boot WebMvc 应用,可用 @Order(1) 的 HandlerInterceptor;如果是 WebFlux,用 GlobalFilter + Mono.defer() 封装限流逻辑。关键点是把限流判断放在最外层,早拒绝、少穿透。
- WebMvc 中:preHandle 返回 false 即中断请求,直接 setStatus(429)
- WebFlux 中:filter 内部用 Mono.fromCallable(() -> limiter.tryAcquire()).flatMap(b -> b ? chain.filter(exchange) : Mono.error(new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS)))
- 异常统一处理:捕获自定义限流异常,在 @ControllerAdvice 中转成标准 JSON 响应体



















