php redis 自增计数器,php使用lua+redis实现限流,计数器模式,令牌桶模式

lua 优点

减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;

原子操作: Redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务;

复用: 脚本会永久保存 Redis 中, 其他客户端可继续使用.

计数器模式:

利用lua脚本一次性完成处理达到原子性,通过INCR自增计数,判断是否达到限定值,达到限定值则返回限流,添加key过期时间应该范围过度

$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 请求代码:

Redis::eval($lua, 3, sprintf(RedisKey::API_LIMIT, $key, $callService['service']), 60, $cache_key);

令牌桶模式

每次请求在桶内拿取一个令牌,有令牌则通过,否则返回,并且按照算法一定的慢慢把令牌放入桶内

$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脚本, 2 KEYS数量, 3 查找数据key, 4 限制key, 5 桶内数量, 6 时间戳, 7 过期时间

Redis::eval(1,2,3,4,5,6,7参数);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

### 使用 Lua 脚本在 Redis实现限流 为了实现Redis使用 Lua 脚本来进行限流,可以通过设置时间窗口内的请求次数上限来控制访问频率。下面是一个简单的例子,展示了如何利用 LuaRedis 来创建一个令牌桶算法的限流机制。 #### 令牌桶算法简介 令牌桶是一种常见的流量整形技术,通过设定固定的速率向桶内投放一定数量的令牌,每次处理请求前先尝试消耗一个或多个令牌;如果桶里有足够的令牌,则允许继续处理该次请求并减少相应数目的令牌;反之则拒绝服务或者延迟响应直到有新的令牌可用为止[^2]。 #### 实现代码示例 以下是基于上述原理编写的 Lua 脚本,在 Redis实现了每分钟最多 N 次请求的限制: ```lua -- 定义参数:key, limit, duration (单位秒), now_timestamp local key = KEYS[1] local limit = tonumber(ARGV[1]) local duration = tonumber(ARGV[2]) -- 时间窗长度 local current_time = tonumber(ARGV[3]) -- 获取当前计数值和过期时间戳 local count_and_expire = redis.call('GET', key) if not count_and_expire then -- 如果不存在记录,则初始化为0,并设置TTL redis.call("SET", key, "1") redis.call("EXPIRE", key, duration) else local parts = {} for str in string.gmatch(count_and_expire, "%S+") do table.insert(parts, tonumber(str)) end local old_count = parts[1] local expire_at = parts[2] if current_time >= expire_at then -- 当前时间超过上次更新的时间上持续周期时重置计数器 redis.call("SET", key, "1") redis.call("EXPIRE", key, duration) elseif old_count < limit then -- 否则计数器redis.call("INCRBY", key, 1) else return {false} -- 达到限额返回错误信息 end end return {true} ``` 此脚本接受四个输入参数: - `KEYS[1]` 是用于存储特定客户端 IP 地址或其他唯一标识符对应的计数器名称; - `ARGV[1]` 表示最大允许的请求数量(即限额); - `ARGV[2]` 设定了时间窗口大小(以秒计算),比如60表示一分钟; - `ARGV[3]` 提供了一个 Unix 时间戳作为当前时刻参考点。 调用这个 Lua 函数之前需要准备好以上提到的所有变量,并将其传递给 EVAL 或者 EVALSHA 命令执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值