Java网关限流案例

wen java案例 1

本文目录导读:

Java网关限流案例

  1. 目录导读
  2. 限流为什么必须做在网关层?
  3. 四大经典限流算法对比
  4. Java网关限流落地案例:Spring Cloud Gateway + Redis + Lua
  5. 生产级进阶方案:阿里Sentinel网关流控
  6. 常见限流配置问答(FAQ)

目录导读

  1. 限流为什么必须做在网关层? —— 核心痛点与架构定位
  2. 四大经典限流算法对比 —— 计数器、滑动窗口、漏桶、令牌桶
  3. Java网关限流落地案例 —— Spring Cloud Gateway + Redis + Lua 脚本实战
  4. 生产级进阶方案 —— 阿里Sentinel网关流控与热点参数限流
  5. 常见限流配置问答(FAQ) —— 阈值设置、异常处理、动态调整

限流为什么必须做在网关层?

在微服务架构中,网关是所有请求的唯一入口(如Spring Cloud Gateway、Zuul),如果将限流分散到各个业务服务,会导致资源浪费(每个服务都要写一遍)且难以统一管控(阈值不一致、无法全局联动),网关限流能实现:

  • 全局视角:统一控制QPS、并发线程数,防止单个服务被突发流量打垮后雪崩。
  • 前置拦截:无效请求在入口即被丢弃,避免穿透到下游数据库或第三方接口。
  • 成本可控:仅需在网关层维护一套限流组件,与业务代码完全解耦。

真实案例:某电商大促期间,下单接口突发10倍流量,若未在网关限流,订单服务数据库连接池瞬间被占满,连锁导致支付、库存服务全部超时,网关层配置 单机QPS=2000 后,超出请求直接返回 429 Too Many Requests,核心服务稳定运行。


四大经典限流算法对比

算法 原理 优点 缺点 适用场景
固定窗口计数器 每秒重置计数 实现简单 临界问题(窗口切换瞬间突发2倍流量) 简单非严格限流
滑动窗口日志 记录每个请求时间戳,滑动统计 无临界问题 内存占用高 低并发环境
漏桶算法 请求入桶,恒定速率流出 绝对平滑 无法应对突发流量(限制了峰值) 保护下游流量整形
令牌桶算法 桶内放令牌,取到才放行 允许一定突发,平滑控速 实现稍复杂 网关首选(如Guava RateLimiter)

关键结论:网关限流通常采用令牌桶 + 滑动窗口组合,即用令牌桶控制平均速率,用滑动窗口防止瞬时临界值穿透,Sentinel底层正是融合了两者。


Java网关限流落地案例:Spring Cloud Gateway + Redis + Lua

1 为什么用Redis + Lua?

  • 原子性:Redis执行Lua脚本是原子操作,避免并发下计数覆盖。
  • 高性能:Lua脚本在Redis服务器内执行,减少网络往返(相比Java代码多次get/set)。
  • 可分布式:多网关实例共享Redis,实现全局配额。

2 核心Lua脚本(令牌桶)

local key = KEYS[1]           -- 用户或接口限流key
local limit = tonumber(ARGV[1]) -- 令牌桶容量
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then
    return 0  -- 拒绝
else
    redis.call('incr', key)
    redis.call('expire', key, ARGV[2]) -- 超时自动清空
    return 1  -- 放行
end

3 Java配置过滤器(伪代码)

@Component
public class RateLimitFilter implements GlobalFilter, Ordered {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String clientIp = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
        String key = "rate_limit:" + clientIp;
        // 调用Lua脚本,判断是否放行
        Long result = redisTemplate.execute(rateLimitScript, 
                Arrays.asList(key), "10", "60"); // 10次/60秒
        if (result == 0) {
            exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }
}

4 配置效果测试

  • 压测工具:JMeter 模拟1000个并发请求。
  • 结果:前10个请求成功(200),后续请求全部返回429,Redis内key的TTL自动滑动,实现每个IP 10次/分钟的独立限额。

生产级进阶方案:阿里Sentinel网关流控

当业务复杂(需要按用户、按URL、按来源APP限流)时,手写Lua脚本已捉襟见肘。Sentinel 提供开箱即用的网关适配器:

  • 精细维度:可针对 API分组请求头参数 做流控。
  • 动态规则:通过控制台实时调整阈值,无需重启网关。
  • 熔断降级:限流触发后自动返回兜底结果(如JSON错误体),避免堆栈溢出。

配置示例(控制台):

资源名: /order/**
阈值类型: QPS
单机阈值: 500
流控效果: 快速失败 + 预热(默认1000毫秒内缓慢爬坡)

经验分享:大促前将阈值设为正常峰值的120%,并开启“排队等待”(漏桶模式)吸收瞬时毛刺,比简单丢弃更稳定。


常见限流配置问答(FAQ)

Q1: 限流阈值设置多少合适?
A: 通过压测获取下游应用最大可承受TPS的80%,过高则保护失效,过低则误杀正常流量,建议分阶梯:核心接口严格限边缘接口宽松限

Q2: 被限流的请求如何处理?
A: 最佳实践是返回 429 + Retry-After 头(告知客户端几秒后重试),并记录日志用于监控,不要静默返回空数据,否则前端可能出现空白页面。

Q3: 网关限流与业务限流冲突吗?
A: 必须设计为网关粗粒度(保护整体入口)、业务细粒度(保护具体资源)的层次结构,网关限流异常时,业务侧可降级但不重复限流。

Q4: 如果Redis宕机,限流会失效吗?
A: 必须设置本地兜底(如Guava RateLimiter缓存最近100ms的令牌),建议开启Redis高可用(哨兵/集群),并将网关本地限流阈值调整为全局的80%作为fallback。

Q5: 如何观察限流是否有效?
A: 接入监控看板(Prometheus + Grafana),统计 rejected_request_totalpassed_request_total 两个指标,理想状态是曲线平滑无毛刺,且拒绝量占比 < 5%(说明阈值合理)。


Java网关限流是保障微服务稳定性的第一道防线,从手写Redis+LuaSentinel托管,核心是理解算法本质(令牌桶负责平滑,滑动窗口防临界),并做好多级降级(本地fallback)、监控告警动态调参,实际项目中,建议先用简单计数器快速上线,再逐步演进到Sentinel全功能——先活下来,再活得优雅。

抱歉,评论功能暂时关闭!