当前位置:   article > 正文

go-zero源码阅读-固定时间窗口限流_newperiodlimit

newperiodlimit

一. 简介

go-zero框架的PeriodLimit限流是固定时间窗口限流,通过lua脚本操作redis, 对key进行计数并搭配过期时间实现限流

二. 源码

1. 代码路径

core/limit/periodlimit.go

2.  核心struct

  1. type (
  2. // PeriodOption defines the method to customize a PeriodLimit.
  3. // PeriodLimit进行参数设置
  4. PeriodOption func(l *PeriodLimit)
  5. // A PeriodLimit is used to limit requests during a period of time.
  6. PeriodLimit struct {
  7. // 时间窗口大小,单位: 秒
  8. period int
  9. // 限流阈值
  10. quota int
  11. // 依赖redis实现
  12. limitStore *redis.Redis
  13. // key前缀
  14. keyPrefix string
  15. align bool
  16. }
  17. )

结构体字段围绕redis的set命令设计, align 为false时,时间窗口是固定的值period, 如果为true则是周期性的(如period=3时: 3->2->1->3->2。。。)

3. 限流方法

  1. // Take requests a permit, it returns the permit state.
  2. func (h *PeriodLimit) Take(key string) (int, error) {
  3. return h.TakeCtx(context.Background(), key)
  4. }
  5. // TakeCtx requests a permit with context, it returns the permit state.
  6. func (h *PeriodLimit) TakeCtx(ctx context.Context, key string) (int, error) {
  7. // 执行lua脚本进行限流判断
  8. // ARGV为限流阈值和周期
  9. resp, err := h.limitStore.EvalCtx(ctx, periodScript, []string{h.keyPrefix + key}, []string{
  10. strconv.Itoa(h.quota),
  11. // 调用方法获取限流周期
  12. strconv.Itoa(h.calcExpireSeconds()),
  13. })
  14. if err != nil {
  15. return Unknown, err
  16. }
  17. // 判断限流结果
  18. code, ok := resp.(int64)
  19. if !ok {
  20. return Unknown, ErrUnknownCode
  21. }
  22. switch code {
  23. case internalOverQuota:
  24. return OverQuota, nil
  25. case internalAllowed:
  26. return Allowed, nil
  27. case internalHitQuota:
  28. return HitQuota, nil
  29. default:
  30. return Unknown, ErrUnknownCode
  31. }
  32. }

核心逻辑就是执行lua脚本进行限流检测,最后校验方法返回值,判断是否限流

4. lua脚本

  1. const periodScript = `local limit = tonumber(ARGV[1]) // 获取阈值
  2. local window = tonumber(ARGV[2]) // 获取限流窗口
  3. local current = redis.call("INCRBY", KEYS[1], 1) // 限流计数, key 加1
  4. // 只有值为1的时候,进行过期时间设置
  5. // 通过过期时间 控制限流窗口,key过期后,重新计数,进入新的窗口
  6. if current == 1 then
  7. redis.call("expire", KEYS[1], window)
  8. end
  9. // 限流阈值判断
  10. if current < limit then
  11. return 1
  12. elseif current == limit then
  13. return 2
  14. else
  15. return 0
  16. end`

通过对key进行计数,key的值为1时,设置过期时间,来达到固定时间窗口限流的目的

5. 初始化配置设置

  1. type (
  2. // PeriodOption defines the method to customize a PeriodLimit.
  3. // 参数设置 函数
  4. PeriodOption func(l *PeriodLimit)
  5. )
  6. func NewPeriodLimit(period, quota int, limitStore *redis.Redis, keyPrefix string,
  7. opts ...PeriodOption) *PeriodLimit {
  8. limiter := &PeriodLimit{
  9. period: period,
  10. quota: quota,
  11. limitStore: limitStore,
  12. keyPrefix: keyPrefix,
  13. }
  14. // 传入函数后,一一执行,由外部控制参数设置
  15. for _, opt := range opts {
  16. opt(limiter)
  17. }
  18. return limiter
  19. }
  20. // 参数设置
  21. func Align() PeriodOption {
  22. return func(l *PeriodLimit) {
  23. l.align = true
  24. }
  25. }

由初始化方法传入 多个配置设置函数进行配置设置, 由外部控制配置设置,增加了初始化配置的灵活性

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/274630?site
推荐阅读
相关标签
  

闽ICP备14008679号