PeriodLimit If you were writing a bug management system, using this PeriodLimit you could limit each tester to only giving you one bug per day. Wouldn’t it make the job a lot easier? 😛

Micro service architecture is popular now essential reason is that to reduce the overall complexity of the system, the system risk sharing to subsystem so as to maximize the guarantee the stability of the system through the field of each subsystem is divided into different subsystems after can independent development, testing, release, can obviously increase the pace of development and efficiency.

However, it also brings problems, such as long call links, increased complexity of deployment architecture, and the need for various middleware to support distributed scenarios. In order to ensure the normal operation of microservices, service governance is indispensable, which usually includes: limiting traffic, downgrading, and circuit breakers.

Flow limiting refers to limiting the interface call frequency so as not to bring down the system when the load limit is exceeded. Such as:

  1. Second kill scenario of e-commerce
  2. API limits streams for different merchants

Common traffic limiting algorithms are as follows:

  • Fixed time window current limiting
  • Sliding time window current limiting
  • Bucket current limit
  • Token bucket flow limiting

This paper mainly explains the fixed-time window flow limiting algorithm, and the main application scenarios are as follows:

  • Each mobile phone number can only send five verification code messages per day
  • Each user is limited to three consecutive password attempts per hour
  • Each member can only receive benefits three times a day

The working principle of

At the same time, judge whether the number of requests in the current time window exceeds the limit. If the number exceeds the limit, the request will be rejected. Then at the beginning of the next time window, the counter will clear the waiting request to zero.

The advantages and disadvantages

advantages

The implementation is simple and efficient, especially suitable for limiting scenarios such as a user can only send 10 articles a day, can only send SMS verification code 5 times, can only try to log in 5 times, etc. Such scenarios are very common in actual services.

disadvantages

The disadvantage of fixed time window limiting is that it cannot handle the critical area request burst scenario.

Assume that the traffic is limited to 100 requests every 1s, and the user initiates 200 requests within 1s in the middle 500ms. In this case, all 200 requests can pass. This is inconsistent with our expectation of 1s current limit 100 times, the root cause is that the fine granularity of current limit is too coarse.

Go – Zero code implementation

core/limit/periodlimit.go

Redis expiration times are used in Go-Zero to simulate fixed time Windows.

Redis Lua script:

-- KYES[1]: key of the current limiter
-- ARGV[1]:qos, maximum number of requests per unit time
-- ARGV[2]: unit current-limiting window time
-- Maximum number of requests, equal to P.kuota
local limit = tonumber(ARGV[1])
-- Window is a unit of limiting period, here using expiration simulation window effect, equal to P. permit
local window = tonumber(ARGV[2])
-- Number of requests +1 to get the total number of requests
local current = redis.call("INCRBY",KYES[1].1)
If it is the first request, set the expiration time and return success
if current == 1 then
  redis.call("expire",KYES[1],window)
  return 1
Success is returned if the current number of requests is less than limit
elseif current < limit then
  return 1
-- Returns the last request if the current number of requests ==limit
elseif current == limit then
  return 2
Number of requests >limit returns a failure
else
  return 0
end
Copy the code

Fixed time window current limiter definition

type (
  // PeriodOption defines the method to customize a PeriodLimit.
  // The common option parameter mode in go
  // If there are too many parameters, this mode is recommended
  PeriodOption func(l *PeriodLimit)

  // A PeriodLimit is used to limit requests during a period of time.
  // Fixed time window current limiter
  PeriodLimit struct {
    // Window size, in s
    period     int
    // Request the upper limit
    quota      int
    / / store
    limitStore *redis.Redis
    / / key prefix
    keyPrefix  string
    // Linear limiting. If this option is enabled, periodic limiting can be achieved
    // For example, if quota=5, the actual value of quota may be 5.4.3.2.1 periodically
    align      bool})Copy the code

Note that the align parameter, align=true, changes the request limit periodically. For example, when quota=5, the actual quota may be 5.4.3.2.1 periodically

Current limit logic

In fact, the flow limiting logic in the above lua script implementation, need to pay attention to the return value

  • 0: indicates an error, such as redis failure or overload
  • 1: allow
  • 2: yes, but the current window has reached the upper limit, if the batch service is running, then you can sleep and wait for the next window (the author considers very carefully).
  • 3: to refuse
// Take requests a permit, it returns the permit state.
// Perform traffic limiting
// Notice the return value:
// 0: indicates an error, such as redis failure, overload
// 1: allowed
// 2: allows but the current window has reached the upper limit
// 3: reject
func (h *PeriodLimit) Take(key string) (int, error) {
  // Execute the lua script
  resp, err := h.limitStore.Eval(periodScript, []string{h.keyPrefix + key}, []string{
    strconv.Itoa(h.quota),
    strconv.Itoa(h.calcExpireSeconds()),
  })
  
  iferr ! =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
  }
}
Copy the code

This fixed window limiting may be used to limit for example, a user can only send capTCHA SMS 5 times a day. In this case, we need to correspond to the Chinese time zone (GMT+8), and actually the limiting time should start from zero, at which point we need extra alignment (set align to true).

// Calculate the expiration time that is the window time size
/ / if the align = = true
// Linear limiting. If this option is enabled, periodic limiting can be achieved
// For example, if quota=5, the actual value of quota may be 5.4.3.2.1 periodically
func (h *PeriodLimit) calcExpireSeconds(a) 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
}
Copy the code

The project address

Github.com/zeromicro/g…

Welcome to Go-Zero and star support us!

Wechat communication group

Pay attention to the public account of “micro-service Practice” and click on the exchange group to obtain the QR code of the community group.