共計 1815 個字符,預計需要花費 5 分鐘才能閱讀完成。
自動寫代碼機器人,免費開通
這篇文章主要介紹 redis 能夠采用什么樣的方式來實現限流,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
目的:
實現訪問頻率限制
實現訪問者 $ip 在一定的時間 $time 內只能訪問 $limit 次
非腳本實現
private boolean accessLimit(String ip, int limit, int time, Jedis jedis) { boolean result = true; String key = rate.limit: + ip; if (jedis.exists(key)) { long afterValue = jedis.incr(key); if (afterValue limit) { result = false; } } else { Transaction transaction = jedis.multi(); transaction.incr(key); transaction.expire(key, time); transaction.exec(); } return result; }
以上代碼有兩點缺陷
可能會出現競態條件: 解決方法是用 WATCH 監控 rate.limit:$IP 的變動, 但較為麻煩; 以上代碼在不使用 pipeline 的情況下最多需要向 Redis 請求 5 條指令, 傳輸過多.
Lua 腳本實現
Redis 允許將 Lua 腳本傳到 Redis 服務器中執行, 腳本內可以調用大部分 Redis 命令, 且 Redis 保證腳本的原子性:
首先需要準備 Lua 代碼: script.lua
--
-- Created by IntelliJ IDEA.
-- User: jifang
-- Date: 16/8/24
-- Time: 下午 6:11 -- local key = rate.limit: .. KEYS[1] local limit = tonumber(ARGV[1]) local expire_time = ARGV[2] local is_exists = redis.call(EXISTS , key) if is_exists == 1 then if redis.call(INCR , key) limit then return 0 else return 1 end else redis.call(SET , key, 1) redis.call(EXPIRE , key, expire_time) return 1 end
Java
private boolean accessLimit(String ip, int limit, int timeout, Jedis connection) throws IOException { List String keys = Collections.singletonList(ip); List String argv = Arrays.asList(String.valueOf(limit), String.valueOf(timeout)); return 1 == (long) connection.eval(loadScriptString( script.lua), keys, argv); } // 加載 Lua 代碼 private String loadScriptString(String fileName) throws IOException { Reader reader = new InputStreamReader(Client.class.getClassLoader().getResourceAsStream(fileName)); return CharStreams.toString(reader); }
Lua 嵌入 Redis 優勢:
減少網絡開銷: 不使用 Lua 的代碼需要向 Redis 發送多次請求, 而腳本只需一次即可, 減少網絡傳輸; 原子操作: Redis 將整個腳本作為一個原子執行, 無需擔心并發, 也就無需事務; 復用: 腳本會永久保存 Redis 中, 其他客戶端可繼續使用。
以上是“redis 能夠采用什么樣的方式來實現限流”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注丸趣 TV 行業資訊頻道!
向 AI 問一下細節
正文完