滑動窗口算法
指定時間T內,只允許發生N次。我們可以將這個指定時間T,看成一個滑動時間窗口(定寬)。
我們
采用Redis的zset基本數據類型的score來圈出這個滑動時間窗口。在實際操作zset的過程中,我們只需要保留在這個滑動時間窗口以內的數據,其他的數據不處理即可。
- 每個用戶的行為采用一個zset存儲,score為毫秒時間戳,value也使用毫秒時間戳(比UUID更加節省內存)
- 只保留滑動窗口時間內的行為記錄,如果zset為空,則移除zset,不再占用內存(節省內存)
pipeline代碼實現
代碼的實現的邏輯是統計滑動窗口內zset中的行為數量,並且與閾值maxCount直接進行比較就可以判斷當前行為是否被允許。這里涉及多個redis操作,因此使用pipeline可以大大提升效率
public class SimpleSlidingWindowByZSet { private Jedis jedis; public SimpleSlidingWindowByZSet(Jedis jedis) { this.jedis = jedis; } /** * 判斷行為是否被允許 * * @param userId 用戶id * @param actionKey 行為key * @param period 限流周期 * @param maxCount 最大請求次數(滑動窗口大小) * @return */ public boolean isActionAllowed(String userId, String actionKey, int period, int maxCount) { String key = this.key(userId, actionKey); long ts = System.currentTimeMillis(); Pipeline pipe = jedis.pipelined(); pipe.multi(); pipe.zadd(key, ts, String.valueOf(ts)); // 移除滑動窗口之外的數據 pipe.zremrangeByScore(key, 0, ts - (period * 1000)); Response<Long> count = pipe.zcard(key); // 設置行為的過期時間,如果數據為冷數據,zset將會刪除以此節省內存空間 pipe.expire(key, period); pipe.exec(); pipe.close(); return count.get() <= maxCount; }