php uses lua+redis to implement current limiting counter mode and token bucket mode
- 2021-12-04 18:26:44
- OfStack
Advantages of lua
Reduce network overhead: code that does not use Lua needs to send multiple requests to Redis, while scripts only need one time, reducing network transmission;
Atomic operation: Redis executes the whole script as an atom, without worrying about concurrency, so there is no need for transactions;
Reuse: The script will be permanently saved in Redis, and other clients can continue to use it.
Counter mode:
Using lua script to complete the processing once to achieve atomicity, through INCR self-increasing count, judge whether it reaches the limit value, reach the limit value, return to current limit, add key expiration time should be excessive range
$lua = '
local i = redis.call("INCR", KEYS[1])
if i > 10 then
return "wait"
else
if i == 1
then
redis.call("expire", KEYS[1], KEYS[2])
end
return redis.call("get", KEYS[3])
end
';
laravel request code:
Redis::eval($lua, 3, sprintf(RedisKey::API_LIMIT, $key, $callService['service']), 60, $cache_key);
Token bucket mode
Take 1 token in the bucket every time you request, pass if there is a token, otherwise return, and slowly put the token into the bucket according to Algorithm 1
$lua = '
local data = redis.call("get", KEYS[2])
if data then
local dataJson = cjson.decode(data)
local newNum = math.min(KEYS[3], math.floor(((dataJson["limitVal"] - 1) + (KEYS[3]/KEYS[5]) * (KEYS[4] - dataJson["limitTime"]))))
if newNum > 0 then
local paramsJson = cjson.encode({limitVal=newNum,limitTime=KEYS[4]})
redis.call("set", KEYS[2], paramsJson)
return redis.call("get", KEYS[1])
end
return "wait"
end
local paramsJson = cjson.encode({limitVal=KEYS[3],limitTime=KEYS[4]})
redis.call("set", KEYS[2], paramsJson)
return redis.call("get", KEYS[1])
';
// 1. lua Script , 2 KEYS Quantity , 3 Find data key, 4 Limit key, 5 Quantity in barrel , 6 Time stamp , 7 Expired time
Redis::eval(1,2,3,4,5,6,7 Parameter );