一、背景与目标 在微服务架构中,网关作为流量入口,需要具备基础的防护能力。本文介绍一种基于 Redis + Lua 脚本 的分布式 IP 限流方案,在网关层实现每个 IP 每秒最多 N 次请求 的限制,防止恶意刷接口、暴力破解等行为。
核心目标 目标 说明 分布式限流 网关多节点共享计数,精确全局限流 高性能 单次 Redis 调用完成所有操作 原子性 Lua 脚本保证无竞态条件 降级保护 Redis 异常时自动放行,不影响业务 可观测 日志记录真实计数,反映攻击猛烈程度
二、整体架构 ┌─────────────┐ │ 客户端 │ │ IP: 1.2.3.4│ └──────┬──────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Spring Cloud Gateway │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ IpRateLimitGlobalFilter │ │ │ │ ① 提取真实 IP(X-Forwarded-For 优先) │ │ │ │ ② 构造 Key: rate_limit:ip:{IP}:{秒时间戳} │ │ │ │ ③ 执行 Redis Lua 脚本(原子 INCR + EXPIRE) │ │ │ │ ④ 判断: count > limit ? 429 : 放行 │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Redis │ │ │ │ Key: rate_limit:ip:1.2.3.4:1734567890 │ │ Value: 3 (当前秒内该 IP 已请求 3 次) │ │ TTL: 1秒 │ └─────────────────────────────────────────────────────────────┘三、核心实现 3.1 限流配置(application.yml) rate-limit : ip : enabled : true limit : 30 # 每秒最多允许 30 次请求 3.2 核心代码 配置类 @Component @ConfigurationProperties ( prefix= "rate-limit.ip" ) public class IpRateLimitProperties { private boolean enabled= true ; private int limit= 30 ; // getter / setter } Lua 脚本(核心) local key= KEYS[ 1 ] local ttl= tonumber ( ARGV[ 2 ] ) -- 原子性自增 local current= redis. call ( 'INCR' , key) -- 首次访问设置过期时间 if current== 1 then redis. call ( 'EXPIRE' , key, ttl) end -- 返回真实计数 return current限流过滤器 @Component public class IpRateLimitGlobalFilter implements GlobalFilter , Ordered { private static final int WINDOW_SECONDS = 1 ; private static final String LUA_SCRIPT = "local key = KEYS[1]\n" + "local ttl = tonumber(ARGV[2])\n" + "local current = redis.call('INCR', key)\n" + "if current == 1 then\n" + " redis.call('EXPIRE', key, ttl)\n" + "end\n" + "return current" ; private final DefaultRedisScript < Long > limitScript= new DefaultRedisScript < > ( LUA_SCRIPT , Long . class ) ; @Override public Mono < Void > filter ( ServerWebExchange exchange, GatewayFilterChain chain) { if ( ! rateLimitProperties. isEnabled ( ) ) { return chain. filter ( exchange) ; } String ip= getRealIp ( exchange. getRequest ( ) ) ; int limit= rateLimitProperties. getLimit ( ) ; String key= "rate_limit:ip:" + ip+ ":" + Instant . now ( ) . getEpochSecond ( ) ; return Mono . from ( redisTemplate. execute ( limitScript, Collections . singletonList ( key) , String . valueOf ( limit) , String . valueOf ( WINDOW_SECONDS ) ) ) . flatMap ( currentCount-> { if ( currentCount!= null && currentCount> limit) { log. warn ( "IP限流触发 - IP: {}, 计数: {}, 阈值: {}/s" , ip, currentCount, limit) ; return buildTooManyRequestsResponse ( exchange) ; } return chain. filter ( exchange) ; } ) . onErrorResume ( e-> { log. error ( "Redis异常,降级放行 - IP: {}" , ip, e) ; return chain. filter ( exchange) ; } ) ; } @Override public int getOrder ( ) { return Ordered . HIGHEST_PRECEDENCE ; // 最高优先级 } } 四、为什么用 Lua 脚本? 方式 原子性 Redis 调用次数 并发安全 Java 两步操作(INCR + EXPIRE) ❌ 2 次 有竞态 Redis 事务(MULTI + EXEC) ⚠️ 2 次 只保证顺序,不保证原子 Lua 脚本 ✅ 1 次 ✅ 单线程原子执行
关键点:Redis 执行 Lua 脚本时,整个脚本是一个原子操作,不会被其他命令打断。
五、关键设计决策 设计点 选择 原因 限流维度 按 IP 最常见的防刷维度,区分不同客户端 窗口算法 固定窗口(1秒) 实现最简单,性能最高,防刷够用 存储引擎 Redis 网关多节点共享计数,保证一致性 Key 设计 rate_limit:ip:{IP}:{秒时间戳}自然分片,自动过期,无需手动清理 返回值 真实计数 日志可观测攻击严重程度,方便调优 降级策略 Redis 异常时放行 限流是辅助功能,不影响核心业务
六、压测验证 测试配置 rate-limit : ip : enabled : true limit : 30 JMeter 测试结果 线程数 Ramp-Up 预期放行 实际放行 结论 50 0s 30 30 ✅ 精准 30 0s 30 30 ✅ 精准 10 1s ~8(跨窗口) 8 ✅ 合理
运行日志 DEBUG IP限流检查 - IP: 127.0.0.1, Key: rate_limit:ip:127.0.0.1:1734567890, Limit: 30/s WARN IP限流触发 - IP: 127.0.0.1, 当前计数: 31, 阈值: 30/s WARN IP限流触发 - IP: 127.0.0.1, 当前计数: 45, 阈值: 30/s七、注意事项 7.1 Nginx 需透传真实 IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr;7.2 Redis 连接池配置 spring : redis : host : ${ REDIS_HOST} port : 6379 timeout : 2000mslettuce : pool : max-active : 20 max-idle : 10 min-idle : 5 7.3 限流阈值调优 场景 建议阈值 说明 普通 API 30-50/s 覆盖正常用户浏览行为 登录/注册接口 3-5/s 防暴力破解 批量查询接口 50-100/s 列表加载等场景
八、方案优缺点 ✅ 优点 极简高效 :一个 Filter + 一个 Lua 脚本搞定分布式支持 :Redis 共享计数,网关集群一致原子性保障 :Lua 脚本保证无竞态降级保护 :Redis 异常不影响业务可观测性强 :日志记录真实计数⚠️ 局限 固定窗口边界问题 :1 秒末 + 2 秒初允许 2 倍流量(防刷场景可接受)依赖 Redis :需要 Redis 高可用保障单维度限流 :仅按 IP 限流,如需多维度需扩展九、总结 本文介绍了一种生产级分布式 IP 限流方案 ,核心思想是:
在网关层用 Redis + Lua 做原子计数,每个 IP 每秒独立计数,超过阈值返回 429。