分布式限流器框架 eval-rate-limiter

打印 上一主题 下一主题

主题 1486|帖子 1486|积分 4458

分布式限流器框架 eval-rate-limiter


  
前言

基于 redis 实现的分布式限流器,实现如下效果


  • 对每个应用节点进行限流

    • 有需要的可以根据现实业务定制 rateLimiterKey 的生成规则,目前是针对 ip+port 进行限流

  • 控制限流的速率->次数/分钟
源码已上传 github


  • https://github.com/huajiexiewenfeng/eval-rate-limiter
设计

流程图




  • 时间窗口&最大条数可以设置
  • 接纳 redis 的过期时间+increment 来实现时间窗口和计数功能
  • synchronized 防止多线程获取 count 产生一致性问题
  • key 接纳实例的 host+port 来实现各个实例控制本身的发送速率
核心方法

tryAcquire 获取通信证



  • 获取当前的请求次数 currentCount
  • 比较 currentCount 和 MaxCount 的大小

    • 小于,那么 count++,返回 true,表现获取通行证成功
    • 否则,返回 false,表现获取通信证失败

  1.     /**
  2.      * 尝试获取限流通行证
  3.      *
  4.      * @return true表示允许通过,false表示被限流
  5.      */
  6.     public synchronized boolean tryAcquire() {
  7.         if (!properties.getEnable()) {
  8.             return true;
  9.         }
  10.         try {
  11.             int currentCount = getCurrentRequestCount();
  12.             if (currentCount >= properties.getMaxCount()) {
  13.                 logger.warn("Rate limit exceeded - window: {} minutes, max: {}, current: {}",
  14.                         properties.getWindowMinutes(),
  15.                         properties.getMaxCount(),
  16.                         currentCount);
  17.                 return false;
  18.             }
  19.             incrementRequestCount();
  20.             return true;
  21.         } catch (Exception e) {
  22.             logger.error("Failed to acquire rate limit token for key: {}", rateLimiterKey, e);
  23.             // 在限流器异常时默认放行,保证系统可用性
  24.             return true;
  25.         }
  26.     }
复制代码
增加访问次数 incrementRequestCount



  • 利用 redis increment 计数+1
  • 第一次设置过期时间为时间窗口
  1.     private void incrementRequestCount() {
  2.         Long count = valueOperations.increment(rateLimiterKey, 1);
  3.         if (count != null && count == 1) {
  4.             // 首次设置时初始化过期时间
  5.             redisTemplate.expire(rateLimiterKey, properties.getWindowMinutes(), TimeUnit.MINUTES);
  6.         }
  7.     }
复制代码
生身分布式 key generateRateLimiterKey

  1.     private String generateRateLimiterKey() {
  2.         String port = environment.getProperty("server.port", "unknown");
  3.         String host = getLocalHostAddress();
  4.         return String.format("%s:%s:%s", RATE_LIMITER_KEY_PREFIX, host, port);
  5.     }
复制代码
测试

测试代码

  1. @SpringBootApplication
  2. @EnableEvalRateLimiter
  3. @RestController
  4. @RequestMapping("/demo1")
  5. public class Demo1Application {
  6.     @Autowired
  7.     private RateLimiter rateLimiter;
  8.     public static void main(String[] args) {
  9.         SpringApplication.run(Demo1Application.class, args);
  10.     }
  11.     @GetMapping("/test")
  12.     public String test(@RequestParam(value = "count", defaultValue = "105") int count) {
  13.         for (int i = 0; i < count; i++) {
  14.             doTest(i);
  15.         }
  16.         return "hello";
  17.     }
  18.     private void doTest(int count) {
  19.         System.out.println("当前请求次数:" + count + ",节点限流剩余次数:" + rateLimiter.getRemainingRequests());
  20.         if (!rateLimiter.tryAcquire()) {
  21.             // 5秒后再发送
  22.             try {
  23.                 Thread.sleep(5000L);
  24.             } catch (InterruptedException e) {
  25.                 e.printStackTrace();
  26.             }
  27.             doTest(count);
  28.         }
  29.     }
  30. }
复制代码
application.properties 设置
  1. spring.application.name=${APPLICATION_NAME:demo1}
  2. server.port=${SERVER_PORT:8091}
  3. #redis
  4. eval.rate.limiter.redis.database=${REDIS_DB_INDEX:2}
  5. eval.rate.limiter.redis.host=${REDIS_HOST:127.0.0.1}
  6. eval.rate.limiter.redis.port=${REDIS_PORT:6379}
  7. eval.rate.limiter.redis.password=${REDIS_INFRA_PASSWORD:1234567a}
  8. #1分钟
  9. eval.rate.limiter.redis.windowMinutes=${EVAL_RATE_LIMITER_WINDOW_MINUTES:1}
  10. #100次
  11. eval.rate.limiter.redis.maxCount=${EVAL_RATE_LIMITER_MAX_COUNT:100}
复制代码
结果


到 1 分钟时间之后,又重新计数

Redis 客户端



  • key:host+ip,限制每个机器+端口
  • value:每访问一次,value+1,value 到达100(设置)后,不在增加


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

梦见你的名字

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表