redis在滑动窗口的实现
整体思路:每一个行为到来时,都维护一次时间窗口。将时间窗口外的记录全部清理掉,只保留窗口内的记录。zset集合中只有score值非常重要,value值没有特别的意义,只需要保证它是唯一的就可以了。
当然,上述几个连续的redis操作都是针对同一个key的,使用pipeline可以显著提升redis的存取效率,还可以增加事务控制。但这种方案也有缺点,如果这个量很大,如60s内100万次,则不适合做这样的限流,会消耗大量的存储空间。
import redis.clients.jedis.Jedis; public class RedisLimit { // Redis 操作客户端 static Jedis jedis = new Jedis("127.0.0.1", 6379); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 15; i++) { boolean res = isPeriodLimiting("java", 3, 10); if (res) { System.out.println("正常执行请求:" + i); } else { System.out.println("被限流:" + i); } } // 休眠 4s Thread.sleep(4000); // 超过最大执行时间之后,再从发起请求 boolean res = isPeriodLimiting("java", 3, 10); if (res) { System.out.println("休眠后,正常执行请求"); } else { System.out.println("休眠后,被限流"); } } /** * 限流方法(滑动时间算法) * @param key 限流标识 * @param period 限流时间范围(单位:秒) * @param maxCount 最大运行访问次数 * @return */ private static boolean isPeriodLimiting(String key, int period, int maxCount) { long nowTs = System.currentTimeMillis(); // 当前时间戳 // 删除非时间段内的请求数据(清除老访问数据,比如 period=60 时,标识清除 60s 以前的请求记录) jedis.zremrangeByScore(key, 0, nowTs - period * 1000); long currCount = jedis.zcard(key); // 当前请求次数 if (currCount >= maxCount) { // 超过最大请求次数,执行限流 return false; } // 未达到最大请求数,正常执行业务 jedis.zadd(key, nowTs, "" + nowTs); // 请求记录 +1 return true; } }
上一篇:
通过多线程提高代码的执行效率例子
下一篇:
自定义AppBar实现滚动渐变