简单限流

/*** 判断用户行为是否达到阈值** @param userId 用户id* @param action 用户行为* @param maxCount 次数阈值* @param period 限制时间窗口大小* @return*/private boolean is_action_allowed(String userId, String action, long maxCount, int period) {String key = userId + ":" + action;// nowTs ~ targetTs 规定时间窗口long nowTs = System.currentTimeMillis();long edgeTs = nowTs - period * 1000;// 设置新值 zadd key value scorestringRedisTemplate.opsForZSet().add(key, String.valueOf(nowTs), nowTs);// 去除时间窗口外的记录 zremrangebyscore(key, min, max) 删除 score 为 [min, max] 的数据// 时间窗口就是 [beforeTs, nowTs]stringRedisTemplate.opsForZSet().removeRangeByScore(key, 0, edgeTs);// 获取时间窗口内的数量 zcard keyLong count = stringRedisTemplate.opsForZSet().zCard(key);// 设置过期时间,便于后期剔除冷用户stringRedisTemplate.expire(key, period, TimeUnit.SECONDS);if (Objects.isNull(count)) {return true;} else {// 如果时间窗口内的数量小于阈值, 说明可以操作return count <= maxCount;}}
@Testpublic void testLimit() {String userId = "2020";String action = "write";boolean ret;for (int i=0; i<16; i++) {ret = is_action_allowed(userId, action, 20, 10);if (!ret) {System.out.println("限流! 当前次数: " + i);}}}
