首頁  >  文章  >  資料庫  >  redis可以採用什麼方式實現限流

redis可以採用什麼方式實現限流

王林
王林轉載
2021-01-20 09:16:572105瀏覽

redis可以採用什麼方式實現限流

目的:

  • 實作存取頻率限制

  • 實作訪客$ip 在一定的時間$time 內只能存取$limit 次

(學習影片分享:redis影片教學

非腳本實作

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資料庫教學

以上是redis可以採用什麼方式實現限流的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除