简单限流

image.png

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