If you write a bug management system and use this PeriodLimit, you can limit each tester to only mention one bug to you every day. Is work much easier P
The essential reason for the popularity of microservice architecture today is to reduce the overall complexity of the system and spread the system risks to subsystems, so as to maximize the stability of the system. After being divided into different subsystems through domain division, each subsystem can develop, test and release independently, and the R & D rhythm and efficiency can be significantly improved.
But it also brings problems. For example, the call link is too long, the complexity of deployment architecture is increased, and various middleware need to support distributed scenarios. In order to ensure the normal operation of microservices, service governance is indispensable, usually including current limiting, degradation and fusing.
Current limiting refers to limiting the frequency of interface calls to avoid exceeding the upper limit of bearing and dragging down the system. For example:
- E-commerce spike scenario
- API current limit for different merchants
Common current limiting algorithms include:
- Fixed time window current limiting
- Sliding time window current limiting
- Leakage barrel current limiting
- Token bucket current limiting
This paper mainly explains the fixed time window current limiting algorithm.
working principle
Starting from a certain time point, the number of requests for each request is + 1. At the same time, judge whether the number of requests in the current time window exceeds the limit. If it exceeds the limit, reject the request, and then clear the counter at the beginning of the next time window to wait for the request.
Advantages and disadvantages
advantage
The implementation is simple and efficient. It is especially suitable for limiting scenarios, such as a user can only send 10 articles a day, can only send SMS verification code 5 times, and can only try to log in 5 times. Such scenarios are very common in practical business.
shortcoming
The disadvantage of fixed time window current limiting is that it can not handle the burst scenario of critical area requests.
Assuming that the current is limited to 100 requests per 1s, the user initiates 200 requests within 1 s from the middle 500ms. At this time, all 200 requests can pass. This is inconsistent with our expectation of 100 times of 1s current limiting. The root cause is that the fine granularity of current limiting is too coarse.
Go zero code implementation
core/limit/periodlimit.go
redis expiration time is used in go zero to simulate a fixed time window.
redis lua script:
-- KYES[1]:Current limiter key -- ARGV[1]:qos,Maximum requests per unit time -- ARGV[2]:Unit current limiting window time -- Maximum number of requests,be equal to p.quota local limit = tonumber(ARGV[1]) -- Window is a unit current limiting cycle,Here we use expiration to simulate the window effect,be equal to p.permit local window = tonumber(ARGV[2]) -- Number of requests+1,Total number of get requests local current = redis.call("INCRBY",KYES[1],1) -- If this is the first request,Set the expiration time and return success if current == 1 then redis.call("expire",KYES[1],window) return 1 -- If the current number of requests is less than limit Success is returned elseif current < limit then return 1 -- If the current number of requests==limit The last request is returned elseif current == limit then return 2 -- Number of requests>limit Failure is returned else return 0 end
Fixed time window current limiter definition
type ( // PeriodOption defines the method to customize a PeriodLimit. // Common option parameter patterns in go // If there are many parameters, it is recommended to use this mode to set parameters 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 seconds period int // Request upper limit quota int // storage limitStore *redis.Redis // key prefix keyPrefix string // Linear current limiting. When this option is enabled, periodic current limiting can be realized // For example, when quota=5, the actual value of quota may be 5.4.3.2.1, showing periodic changes align bool } )
Note the align parameter. When align=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 implemented in the lua script above. It should be noted that 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 it is a batch business, you can sleep and wait for the next window (the author considers it very carefully)
- 3: Refuse
// Take requests a permit, it returns the permit state. // Perform current limiting // Note the return value: // 0: indicates an error, such as redis failure or overload // 1: Allow // 2: Allowed, but the upper limit has been reached in the current window // 3: Refuse func (h *PeriodLimit) Take(key string) (int, error) { // Execute lua script 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 may be used to limit the current limit. For example, a user can only send verification code SMS five times a day. At this time, we need to correspond to the Chinese time zone (GMT+8), and the current limit time should start from zero. At this time, we need additional alignment (set align to true).
// Calculate the expiration time, that is, the window time size // If align==true // Linear current limiting. When this option is enabled, periodic current limiting can be realized // For example, when quota=5, the actual value of quota may be 5.4.3.2.1, showing periodic changes 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
https://github.com/zeromicro/go-zero
https://gitee.com/kevwan/go-zero
Welcome to go zero and star support us!
Wechat communication group
Focus on the "micro service practice" official account and click on the exchange group to get the community community's two-dimensional code.