本文的初衷僅供自己做備忘筆記, 內(nèi)容大多從網(wǎng)上搜集和整理, 并非都是自己原創(chuàng).
參考的來源我會(huì)在后面注明, 對(duì)于可能遺漏的來源, 還請(qǐng)相關(guān)原創(chuàng)作者提醒, 非常感謝.
在網(wǎng)上搜了不少方案, 最終選擇了參照參考來源1
的文章中的方案3, 進(jìn)行適當(dāng)修改以適應(yīng)當(dāng)前項(xiàng)目.
最終以用戶為單位, 對(duì)其請(qǐng)求接口頻率進(jìn)行控制.
邏輯
把限制邏輯封裝到一個(gè)Lua腳本中读拆,調(diào)用時(shí)只需傳入:key淫半、限制數(shù)量墓卦、過期時(shí)間,調(diào)用結(jié)果就會(huì)指明是否運(yùn)行訪問
lua腳本(這里java字符串對(duì)雙引號(hào)進(jìn)行了處理)
local notexists = redis.call(\"set\", KEYS[1], 1, \"NX\", \"EX\", tonumber(ARGV[2]))
if (notexists) then
return 1
end
local current = tonumber(redis.call(\"get\", KEYS[1]))
if (current == nil) then
local result = redis.call(\"incr\", KEYS[1])
redis.call(\"expire\", KEYS[1], tonumber(ARGV[2]))
return result
end
if (current >= tonumber(ARGV[1])) then
error(\"too many requests\")
end
local result = redis.call(\"incr\", KEYS[1])
return result
使用 eval 調(diào)用腳本
eval 腳本 1 key 參數(shù)(允許的最大次數(shù)) 參數(shù)(過期時(shí)間)
正常執(zhí)行時(shí), redis會(huì)返回key對(duì)應(yīng)的數(shù)字, 而超過限制后, redis會(huì)返回error異常
java代碼(部分)
String script = "lua腳本";
String scriptSha = "事先存好的腳本sha"
......
Jedis jedis = JedisPool.getJedisInstance();
if(!jedis.scriptExists(getRedisScriptHash())) {//判斷腳本是否存在
scriptSha = jedis.scriptLoad(lua腳本);//寫入腳本
}
try {
List<String> keys = new ArrayList<>();
keys.add("判斷用戶的key");
List<String> args = new ArrayList<>();
args.add("60");//key的存活時(shí)間, 60秒
jedis.evalsha(scriptSha, keys, args);
} catch (Exception e) {
System.out.println("請(qǐng)求過于頻繁");
......
} finally {
jedis.close();
}