Home  >  Article  >  Backend Development  >  How to implement user daily limit in Go

How to implement user daily limit in Go

藏色散人
藏色散人forward
2022-01-10 15:55:173201browse

This article is written by the golang tutorial column to introduce how to implement the user's daily limit in Go. I hope it will be helpful to friends in need!

Implement the user’s daily limit in Go (for example, you can only receive benefits three times a day)

If you write a bug management system and use this PeriodLimit, you can limit each tester to only submit one bug to you per day. Is work much easier? :P

The essential reason why microservice architecture is so popular nowadays is to reduce the overall complexity of the system, evenly distribute system risks to subsystems to maximize the stability of the system, and split it into different systems through domain division. After the subsystems are installed, each subsystem can be independently developed, tested, and released, and the R&D rhythm and efficiency can be significantly improved.

But it also brings problems, such as: the calling link is too long, the complexity of the deployment architecture increases, and various middleware needs to support distributed scenarios. In order to ensure the normal operation of microservices, service governance is indispensable, which usually includes: current limiting, downgrading, and circuit breaker.

Current limiting refers to limiting the frequency of interface calls to avoid exceeding the load limit and bringing down the system. For example:

  • E-commerce Flash Sale Scenario

  • API current limit for different merchants

Commonly used The current limiting algorithms are:

  • Fixed time window current limiting
  • Sliding time window current limiting
  • Leaky bucket current limiting
  • Token bucket limited Flow

This article mainly explains the fixed time window current limiting algorithm.

Working Principle

Starting from a certain point in time, each request comes with a request count of 1. At the same time, it is judged whether the number of requests in the current time window exceeds the limit. If it exceeds the limit, it will be rejected. The request is then cleared when the next time window begins waiting for the request.

How to implement user daily limit in Go

Advantages and Disadvantages

Advantages

Easy to implement It is efficient and is especially suitable for limiting scenarios such as a user can only post 10 articles a day, can only send SMS verification codes 5 times, and can only try to log in 5 times. Such scenarios are very common in actual business.

Disadvantages

The disadvantage of fixed time window current limiting is that it cannot handle critical section request burst scenarios.

Assume that the current limit is 100 requests every 1 second, and the user initiates 200 requests within 1 second starting from the middle 500ms. At this time, all 200 requests can be passed. This is inconsistent with our expectation of limiting the current to 100 times per second. The root cause is that the fine-grainedness of the current limit is too coarse.

How to implement user daily limit in Go

go-zero code implementation

##core/limit/periodlimit.go

Go-zero uses redis expiration time to simulate a fixed time window.

redis lua script:

-- KYES[1]:限流器key-- ARGV[1]:qos,单位时间内最多请求次数-- ARGV[2]:单位限流窗口时间-- 请求最大次数,等于p.quotalocal limit = tonumber(ARGV[1])-- 窗口即一个单位限流周期,这里用过期模拟窗口效果,等于p.permitlocal window = tonumber(ARGV[2])-- 请求次数+1,获取请求总数local current = redis.call("INCRBY",KYES[1],1)-- 如果是第一次请求,则设置过期时间并返回 成功if current == 1 then
  redis.call("expire",KYES[1],window)
  return 1-- 如果当前请求数量小于limit则返回 成功elseif current limit则返回 失败else
  return 0end
Fixed time window current limiter definition

type (
  // PeriodOption defines the method to customize a PeriodLimit.
  // go中常见的option参数模式
  // 如果参数非常多,推荐使用此模式来设置参数
  PeriodOption func(l *PeriodLimit)

  // A PeriodLimit is used to limit requests during a period of time.
  // 固定时间窗口限流器
  PeriodLimit struct {
    // 窗口大小,单位s
    period     int
    // 请求上限
    quota      int
    // 存储
    limitStore *redis.Redis
    // key前缀
    keyPrefix  string
    // 线性限流,开启此选项后可以实现周期性的限流
    // 比如quota=5时,quota实际值可能会是5.4.3.2.1呈现出周期性变化
    align      bool
  }
)
Pay attention to the align parameter, align= When true, the request upper limit will change periodically.

For example, when quota=5, the actual quota may be 5.4.3.2.1, showing periodic changes

Current limiting logic

In fact, the current limiting logic is above The lua script is implemented. It should be noted that the return value

    0: indicates an error, such as redis failure or overload
  • 1: allowed
  • 2: allowed However, the upper limit has been reached in the current window. If you are running a batch business, you can sleep and wait for the next window (the author has considered it very carefully)
  • 3: Rejection
  • // Take requests a permit, it returns the permit state.
    // 执行限流
    // 注意一下返回值:
    // 0:表示错误,比如可能是redis故障、过载
    // 1:允许
    // 2:允许但是当前窗口内已到达上限
    // 3:拒绝
    func (h *PeriodLimit) Take(key string) (int, error) {
      // 执行lua脚本
      resp, err := h.limitStore.Eval(periodScript, []string{h.keyPrefix + key}, []string{
        strconv.Itoa(h.quota),
        strconv.Itoa(h.calcExpireSeconds()),
      })
    
      if err != nil {
        return Unknown, err
      }
    
      code, ok := resp.(int64)
      if !ok {
        return Unknown, ErrUnknownCode
      }
    
      switch code {
      case internalOverQuota:
        return OverQuota, nil
      case internalAllowed:
        return Allowed, nil
      case internalHitQuota:
        return HitQuota, nil
      default:
        return Unknown, ErrUnknownCode
      }
    }
This fixed window current limit may be used to limit, for example, a user can only send verification code text messages 5 times a day. At this time, we need to correspond to the Chinese time zone (GMT 8), and in fact, the current limit time should start from zero o'clock. At this time, we need Additional alignment (set align to true).

// 计算过期时间也就是窗口时间大小
// 如果align==true
// 线性限流,开启此选项后可以实现周期性的限流
// 比如quota=5时,quota实际值可能会是5.4.3.2.1呈现出周期性变化
func (h *PeriodLimit) calcExpireSeconds() int {
  if h.align {
    now := time.Now()
    _, offset := now.Zone()
    unix := now.Unix() + int64(offset)
    return h.period - int(unix%int64(h.period))
  }

  return h.period
}

Project address

github.com/zeromicro/go-zero

Welcome to use

go-zero and star support us!

The above is the detailed content of How to implement user daily limit in Go. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:learnku.com. If there is any infringement, please contact admin@php.cn delete