Skip to content

两个路由级注解中间件。ratelimiter 用滑动窗口 ZSET + Lua 单次 EVAL 原子限流;repeatsubmit 用 SETNX 抢锁 + 响应 code 判定成功/失败防重复提交。两者都依赖 redis.Init 先就绪。

关键导出

  • ratelimiter.LimitType: LimitTypeGlobal / LimitTypeIP / LimitTypeCluster
  • ratelimiter.Limiter / Init()
  • ratelimiter.RateLimiter(window, count, limitType, timeout, message) gin.HandlerFunc
  • ratelimiter.RateLimiterWithKeyFunc(window, count, fn func(*gin.Context) string, timeout, message) gin.HandlerFunc (动态维度,闭包替 SpEL)
  • repeatsubmit.Submitter / Init()
  • repeatsubmit.RepeatSubmit(interval, message) gin.HandlerFunc
  • 常量 defaultInterval=5s / minInterval=1s

典型用法

go
// internal/system/router.go:62
profile.PUT("/update",
    oplog.Log(profileLogTitle, enum.BusinessTypeUpdate),
    repeatsubmit.RepeatSubmit(0, ""),
    handler.ProfileApiApp.UpdateProfile)

坑/约定

WARNING

  • ratelimiter 滑动窗口用 ZSET + Lua 单次 EVAL 避免「读-判-写」竞态;ZADD 成员必须唯一(同毫秒并发否则覆盖只计一次);Redis 异常时放行(可用性优先);timeout 至少覆盖一个窗口否则键提前消失;LimitTypeCluster 用进程实例 ID(newInstanceID())。
  • repeatsubmit 指纹 = sha256(token + ":" + 请求体 + query)(无 AOP 取不到方法入参);interval < 1s 注册期 panic 而非运行期抛异常;须排在 encrypt.ApiEncrypt() 之后(指纹要用解密明文,密文每次随机 AES 密钥会使指纹漂移);成功(code=200)保留键 interval 内挡重,失败/panic 删键允许重试;panic 路径不 flush 缓冲区(否则 c.Writer.Written()=true,Recover 不再渲染 500,客户端收半截响应);release 用 context.WithoutCancel(客户端断连会取消原 ctx,但键仍要删)。 :::

相关页:/pkg-reference/middleware/pkg-reference/captcha-encrypt/pkg-reference/response-errs

基于 MIT 协议开源