
基于白名单的全局限流插件对指定的域名和URL路径进行全局限流控制共享同一个限流计数器。核心特性按域名 URL路径进行全局限流使用 Redis Sorted Set 实现滑动时间窗口白名单机制只对配置的域名和路径进行限流支持正则表达式匹配URL路径实现逻辑1. 请求处理流程请求到达 → 检查域名白名单 → 检查路径白名单 → Redis限流判断 → 放行/拒绝2. 核心组件配置解析 (parseConfig)解析域名白名单hosts解析路径白名单paths支持正则表达式配置限流参数unitSecond: 统计周期默认30秒qpm: 周期内最大请求数默认10次key: Redis存储的key名称初始化 Redis 客户端连接域名过滤 (SkipHost)只有在白名单中的域名才进行限流未在白名单中的域名直接放行路径过滤 (SkipPath)使用路径过滤器匹配URL支持正则表达式匹配未匹配的路径直接放行3. 限流算法 - 滑动窗口采用Redis Sorted Set Lua 脚本实现滑动窗口限流-- Lua 脚本执行原子操作 1. ZREMRANGEBYSCORE: 删除过期数据分数 now - window 2. ZCOUNT: 获取当前窗口内的请求数 3. 判断 count limit超过则返回1 4. ZADD: 添加新请求score时间戳memberUUID 5. EXPIRE: 设置key过期时间 6. 返回0表示未超限关键特性原子性Lua 脚本保证操作的原子性滑动窗口基于时间戳的精确滑动窗口自动过期过期时间设为统计周期的2倍UUID去重每个请求使用唯一ID作为成员4. 限流响应超过限制时返回HTTP 429 Too Many Requests { code: 429, message: Too Many Requests }配置参数参数类型必填默认值说明serviceNamestring是-Redis服务名称servicePortint是-Redis端口domainstring是-Redis域名usernamestring是-Redis用户名passwordstring是-Redis密码timeoutint否-连接超时时间(ms)hosts[]string是-域名白名单paths[]string是-URL路径白名单支持正则unitSecondint否30统计周期秒qpmint否10周期内最大请求数keystring否global-limit-plugin-keyRedis key名称配置示例serviceName: test-redis-service servicePort: 6379 domain: xxx.redis.rds.aliyuncs.com username: user password: password timeout: 50000 hosts: - api.example.com - www.example.com paths: - /auth/token - /api/sensitive/.* unitSecond: 60 qpm: 100 key: my-global-limit与 Route Limit 的区别特性Global LimitRoute Limit限流维度全局共享计数器每个URL独立计数配置方式统一配置qpm每个URL单独配置适用场景整体流量控制精细化接口限流路径过滤白名单过滤规则匹配使用场景全站流量控制对整个站点进行统一的流量限制核心接口保护对重要接口进行全局访问频率控制防止突发流量在高并发场景下保护后端服务白名单限流只对特定域名和路径进行限流保护核心代码now : time.Now() nowTimestamp : now.Unix() //秒数 intervalTime : int64(config.unitSecond) // 使用 Lua 脚本实现清理过期数据 计数 添加新记录 设置过期时间 // 返回值0 表示未超限1 表示已超限 luaScript : local key KEYS[1] local now tonumber(ARGV[1]) local window tonumber(ARGV[2]) local limit tonumber(ARGV[3]) local member ARGV[4] local expire_time tonumber(ARGV[5]) -- 删除过期数据分数小于 now - window 的成员 redis.call(ZREMRANGEBYSCORE, key, -inf, now - window) -- 获取当前窗口内的请求数 local count redis.call(ZCOUNT, key, now - window, now) if count limit then return 1 -- 超过限制 end -- 添加新请求 redis.call(ZADD, key, now, member) -- 设置key的过期时间防止key永久存在 redis.call(EXPIRE, key, expire_time) return 0 -- 未超过限制 // 准备参数 var keyArr []interface{} keyArr append(keyArr, config.key) var valueArr []interface{} uuid : uuid.New() expireTime : config.unitSecond * 2 // 过期时间设为统计周期的2倍 valueArr append(valueArr, nowTimestamp, intervalTime, config.qpm, uuid.String(), expireTime) // 执行 Lua 脚本 err : config.Client.Eval(luaScript, 1, keyArr, valueArr, func(response resp.Value) { if response.Integer() 1 { // 超过限制 fmt.Println(TOO_MANY_REQUESTS 429 ,path:, ctx.Path(), ,ipAddress:, util.GetClientIP()) headers : [][2]string{{Content-Type, application/json}} proxywasm.SendHttpResponse(429, headers, []byte({\code\:429,\message\:\Too Many Requests\}), -1) } else { // 未超过限制继续请求 proxywasm.ResumeHttpRequest() } }) if err ! nil { log.Errorf(rate limit error while calling redis: %v, err) proxywasm.ResumeHttpRequest() } return types.ActionPause