当前位置: 首页 > news >正文

wasm~tinygo写一个按着路由进行限流的插件

Route Limit 插件

插件功能

基于URL路径的限流插件,可以针对不同的URL路径配置独立的限流规则,每个URL的限流策略相互隔离。

核心特性

  • 支持按URL路径配置独立的限流规则
  • 每个URL可以设置不同的统计周期和请求数限制
  • 支持正则表达式匹配URL路径
  • 依赖 Redis 实现分布式限流

实现逻辑

1. 请求处理流程

请求到达 → 检查域名白名单 → 匹配URL规则 → Redis限流判断 → 放行/拒绝

2. 核心组件

配置解析 (parseConfig)

  • 解析域名白名单 hosts
  • 解析限流规则列表 rules,每个规则包含:
    • path: URL路径(支持正则)
    • unitSecond: 统计周期(秒),默认30秒
    • limit: 周期内最大请求数,默认100次
  • 初始化 Redis 客户端连接

域名过滤 (SkipHost)

  • 检查请求的域名是否在白名单中
  • 如果未配置 hosts,则不限制域名
  • 不在白名单中的域名直接放行

规则匹配 (MatchRule)

  • 遍历配置的限流规则
  • 使用路径过滤器(支持正则)匹配当前请求路径
  • 返回匹配的规则用于限流

3. 限流算法 - 滑动窗口

采用 Redis Sorted Set + Lua 脚本 实现滑动窗口限流:

-- Lua 脚本执行原子操作
1. ZREMRANGEBYSCORE: 清理过期数据(窗口外的记录)
2. ZCOUNT: 统计当前窗口内的请求数
3. 判断是否超过限制
4. ZADD: 添加新请求记录(score=时间戳,member=唯一ID)
5. EXPIRE: 设置key过期时间(防止永久存在)

Redis Key 格式

{keyPrefix}:{path}

示例:route-limit:/user/list

4. 限流响应

超过限制时返回:

HTTP 429 Too Many Requests
{"code": 429,"message": "Too Many Requests","path": "/user/list","limit": "50/30s"
}

配置参数

参数 类型 必填 默认值 说明
serviceName string - Redis服务名称
servicePort int - Redis端口
domain string - Redis域名
username string - Redis用户名
password string - Redis密码
timeout int - 连接超时时间(ms)
keyPrefix string route-limit Redis key前缀
hosts []string - 域名白名单
rules []object - 限流规则列表

Rules 配置

参数 类型 必填 默认值 说明
path string - URL路径(支持正则)
unitSecond int 30 统计周期(秒)
limit int 100 周期内最大请求数

配置示例

serviceName: "test-redis-service"
servicePort: 6379
domain: "xxx.redis.rds.aliyuncs.com"
username: "user"
password: "password"
timeout: 1000
keyPrefix: "route-limit"
hosts:- "api.example.com"
rules:- path: "/auth/token"unitSecond: 60limit: 10- path: "/user/list"unitSecond: 30limit: 50- path: "/product/.*"unitSecond: 30limit: 100

使用场景

  1. 接口级限流:对特定API接口设置精细的限流策略
  2. 敏感接口保护:对登录、注册等敏感接口进行严格限流
  3. 资源隔离:不同接口之间的限流互不影响
  4. 灵活配置:支持正则匹配,方便批量配置同类接口

核心代码

now := time.Now()nowTimestamp := now.Unix() // 秒数intervalTime := int64(rule.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 thenreturn 1  -- 超过限制end-- 添加新请求redis.call('ZADD', key, now, member)-- 设置key的过期时间,防止key永久存在redis.call('EXPIRE', key, expire_time)return 0  -- 未超过限制`// 生成redis key,格式: {keyPrefix}:{path}// 将path中的/替换为:,避免redis key中出现特殊字符redisKey := fmt.Sprintf("%s:%s", config.KeyPrefix, rule.Path)// 准备参数var keyArr []interface{}keyArr = append(keyArr, redisKey)var valueArr []interface{}requestId := uuid.New()expireTime := rule.UnitSecond * 2 // 过期时间设为统计周期的2倍valueArr = append(valueArr, nowTimestamp, intervalTime, rule.Limit, requestId.String(), expireTime)// 记录匹配信息用于调试log.Debugf("Route limit matched: path=%s, rule=%s, limit=%d/%ds", ctx.Path(), rule.Path, rule.Limit, rule.UnitSecond)// 执行 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(), ", rule:", rule.Path, ", limit:", rule.Limit, "/", rule.UnitSecond, "s, ipAddress:", util.GetClientIP())headers := [][2]string{{"Content-Type", "application/json"}}msg := fmt.Sprintf(`{"code":429,"message":"Too Many Requests","path":"%s","limit":"%d/%ds"}`, ctx.Path(), rule.Limit, rule.UnitSecond)proxywasm.SendHttpResponse(429, headers, []byte(msg), -1)} else {// 未超过限制,继续请求proxywasm.ResumeHttpRequest()}})if err != nil {log.Errorf("rate limit error while calling redis: %v, path: %s", err, ctx.Path())proxywasm.ResumeHttpRequest()}return types.ActionPause
http://www.gsyq.cn/news/147338.html

相关文章:

  • 告别“重”投入:中小企业如何选型“小而美”的数据中台?
  • 【紧急排查】Open-AutoGLM突然无法打开浏览器?这份故障清单必须收藏
  • 揭秘Open-AutoGLM融合语音识别核心技术:让设备“听懂”你的每一句话
  • 2025年唐山热门GEO优化公司推荐:靠谱的GEO动态优化公司有哪些? - mypinpai
  • 28V转18V电路设计方案优化
  • 【紧急预警】Open-AutoGLM生产环境黑屏频发,这份热修复方案请立即应用
  • 基于php医院预约挂号系统
  • Open-AutoGLM插件到底有多强?10大应用场景颠覆你的认知
  • hash+滑窗
  • Linux中swap是什么?用途有哪些?
  • 从覆盖率数字到智能风险洞察:大模型如何重塑测试覆盖分析
  • 国产高温陶瓷纤维马弗炉这么多,哪家售后做得好?2025年度源头厂家推荐 - 品牌推荐大师
  • Hybrid Model Support:阿里云 Tair 联合 SGLang对 Mamba-Transformer 等混合架构模型的支持方案
  • 【圣诞快乐 Merry Christmas】酷圣诞粒子特效网页
  • 智谱AutoGLM浏览器调用失败?99%的人都忽略了这1个关键参数
  • 2025年高温合金GH4169服务商家排名:看哪家实力不错? - myqiye
  • 2025年有名的航空运输专业公司推荐:售后完善的航空运输品牌企业有哪些? - 工业推荐榜
  • 03. 内存屏障
  • 2025年比较好的意大利四段力铰链/耐用四段力铰链厂家最新推荐排行榜 - 品牌宣传支持者
  • 《动手构建大模型》零基础进阶大模型实战高手之路,系统学习LLM开发,成为实战型AI工程师!
  • 三大信号、一个前提:判断你企业入场数据中台的最佳时机
  • 计算机毕业设计springboot校园运动场地预约系统 高校体育场地在线预约平台的设计与实现 基于SpringBoot的校园运动场馆智能预订系统
  • 探秘网红景区:灵龙谷——亲子游与高性价比服务的绝佳之选 - mypinpai
  • SwiftUI Accessibility Masterclass
  • 计算机毕业设计springboot同城伴宠平台的设计与实现 基于SpringBoot的同城宠物托管与交流平台研发 SpringBoot驱动的同城宠物互助服务系统的设计与落地
  • 2025年热门遮白发染发剂品牌选购指南:温和安全易操作,植萃滋养款实测 - 资讯焦点
  • 基于微信小程序的个性化点餐系统任务书
  • 基于微信小程序的个性化新闻推荐系统的设计与实现开题报告1每页格式不得改动
  • [css特性]HTML Learn Data Day 3
  • GPT-SoVITS语音风格迁移实战:模仿明星声线全记录