lua的优点减少网络开销:不使用lua的代码需要向redis发送多次请求,而脚本只需要发送一次,减少网络传输;原子操作:Redis将整个脚本作为一个原子来执行,不用担心并发,也不需要Transaction;多路复用:脚本会永久保存在Redis中,其他客户端可以继续使用。计数器方式:使用lua脚本一次性完成处理,实现原子性,并使用INCR自增计数判断是否达到限制值,达到限制值时返回当前限制,加上key过期时间应该结束$lua='locali=redis.call("INCR",KEYS[1])ifi>10thenreturn"wait"elseifi==1thenredis.call("expire",KEYS[1],KEYS[2])endreturnredis.call("get",KEYS[3])end';laravel请求代码:Redis::eval($lua,3,sprintf(RedisKey::API_LIMIT,$key,$callService['service']),60,$cache_key);令牌桶模式每次在桶中取一个令牌,如果有令牌则通过,否则返回,根据算法判断慢慢将令牌放入桶中注:KEY[1]=lookupdatakey(这是我保存在redis中的key,你可以改成返回true,或者其他)KEY[2]=限流keyKEY[3]=桶中的个数KEY[4]=时间戳KEY[5]=过期时间(这个是你桶里的有效时间,时间越长漏铜恢复越慢)Core:localnewNum=math.min(KEYS[3],math.floor(((dataJson["limitVal"]-1)+(KEYS[3]/KEYS[5])*(KEYS[4]-dataJson["limitTime"]))))大概是:math.min(桶中的数,math.floor((((存储在redisbucket数量-1)+(bucket中的数量/过期时间)*(timestamp-redis中存储的时间戳))))$lua='localdata=redis.call("get",KEYS[2])ifdatathenlocaldataJson=cjson.decode(data)localnewNum=math.min(KEYS[3],math.floor(((dataJson["limitVal"]-1)+(KEYS[3]/KEYS[5])*(KEYS[4]-dataJson["limitTime"]))))ifnewNum>0thenlocalparamsJson=cjson.encode({limitVal=newNum,limitTime=KEYS[4]})redis.call("set",KEYS[2],paramsJson)returnredis.call("get",KEYS[1])endreturn"wait"endlocalparamsJson=cjson.encode({limitVal=KEYS[3],limitTime=KEYS[4]})redis.call("set",KEYS[2],paramsJson)returnredis.call("get",KEYS[1])';//1.lua脚本,2KEYS数量,3搜索数据key,4limitkey,5桶内数量,6时间戳,7过期时间Redis::eval(1,2,3,4,5,6,7参数);小idea:将restrictionkey替换成用户唯一的字段(dimension)可以将bucket的个数和timestamp改为动态的用户限流,根据请求的接口方法(dimension)设置个数,设置不同的current根据不同的方法限制尺寸
