# Realization of Distributed Current Limiter by Redis+Lua

Time：2019-10-9

LastModified: 10:37:39 June 14, 2019

It mainly relies on redis + Lua to realize the current limiter. The reason for using Lua is to combine multiple commands as an atomic operation without too much consideration of concurrency.

# counter mode

## principle

Counter algorithm refers to a fixed number of requests allowed to pass in a window time, such as 10 times per second, 500 times per 30 seconds.

The finer the time granularity set, the smoother the current limit will be.

## Realization

The Lua script used

Counter Current Limitation
The minimum unit time supported here is seconds, and millisecond granularity is supported if expire is changed to pexpire.
KEYS [1] string current limiting key
- ARGV [1] int current limit
- ARGV [2] int unit time (seconds)

local cnt = tonumber(redis.call("incr", KEYS[1]))

if (cnt == 1) then
-- The CNT value is 1 to indicate that it did not exist before, so you need to set its expiration time
redis.call("expire", KEYS[1], tonumber(ARGV[2]))
elseif (cnt > tonumber(ARGV[1])) then
return -1
end

return cnt

Return – 1 indicates that the current limit is exceeded, otherwise the number of requests that have passed in the current unit time is returned.

Key may, but is not limited to, the following situations

• IP + interface
• User_id + interface

• Simple implementation

shortcoming

• When the granularity is not fine enough, there will be double requests in the same window time.

Be careful

• Keep time granularity as fine as possible

## Scene analysis

Eg. 1000/3s current limiting

Extreme case 1:

Number of requests in the first second 10

Number of requests in 2 seconds 10

Number of requests in 3 seconds 980

Fourth second request number 900

Number of requests in 5 seconds 100

Number of requests in 6 seconds 0

Note that the total number of requests in 3-5 seconds is as high as 1980.

Extreme case 2:

Number of requests per second 1000

Number of requests in 2 seconds 0

Number of requests in 3 seconds 0

At this point, a large number of rejection requests will occur in the next 2-3 seconds.

# Token Bucket Mode

## principle

Token bucket

1. The bucket holds tokens, has a ceiling, and is full at first.
2. Each request consumes tokens (different number of tokens can be consumed according to different requests)
3. Every other time (at a fixed rate) tokens are placed in the bucket.

The realization of barrels can also be divided into:

• Consumable

Advance token number: previous digging, later jumping

• Unexpected Consumption

Not enough tokens to reject directly

## Realization

Unpreconsumable token bucket implemented here, Lua code:

Token Bucket Current Limitation: Pre-consumption is not supported and the initial bucket is full
KEYS [1] string current limiting key

--Argv [1] int max. capacity
- The number of tokens added per time for ARGV [2] int
-- ARGV [3] int token addition interval (seconds)
-- ARGV [4] int current timestamp

local bucket_capacity = tonumber(ARGV[1])
local now = tonumber(ARGV[4])

Keys to save the last update bucket
local LAST_TIME_KEY = KEYS[1].."_time";
Get the number of tokens in the current bucket
local token_cnt = redis.call("get", KEYS[1])
Maximum time required for complete recovery of barrels

If token_cnt then -- token bucket exists
The last time the bucket was updated
local last_time = redis.call('get', LAST_TIME_KEY)
--Recovery multiple
local multiple = math.floor((now - last_time) / add_interval)
--Recovery token count
local recovery_cnt = multiple * add_token
- Ensure that barrel capacity is not exceeded
local token_cnt = math.min(bucket_capacity, token_cnt + recovery_cnt) - 1

if token_cnt < 0 then
return -1;
end

- Reset the expiration time to avoid key expiration
redis.call('set', KEYS[1], token_cnt, 'EX', reset_time)
redis.call('set', LAST_TIME_KEY, last_time + multiple * add_interval, 'EX', reset_time)

Other -- Token Bucket does not exist
token_cnt = bucket_capacity - 1
Set expiration time to avoid keys persisting
redis.call('set', KEYS[1], token_cnt, 'EX', reset_time);
redis.call('set', LAST_TIME_KEY, now, 'EX', reset_time + 1);
end

The key to the token bucket is the following parameters:

• Bucket maximum capacity
• Number of tokens put in each time
• The interval between tokens

The implementation of token bucket will not cause double flow per unit time in counter mode.

## [video tutorial] configure PHP auto completion prompt of vscode and debug remotely with Xdebug

The default download and installation of vscode does not accurately prompt and detect PHP syntax errors. You need to manually specify the native PHP program path. After configuration, you can check the syntax for errors when saving the file.Open file – > Preferences – > setting, and fill in the PHP execution path, provided that […]