基於 Redis 實現分布式應用限流[轉]


限流的目的是通過對並發訪問/請求進行限速或者一個時間窗口內的的請求進行限速來保護系統,一旦達到限制速率則可以拒絕服務。

前幾天在DD的公眾號,看了一篇關於使用 瓜娃 實現單應用限流的方案,參考《redis in action》 實現了一個jedis版本的,都屬於業務層次限制。 實際場景中常用的限流策略:

    • Nginx接入層限流
      按照一定的規則如帳號、IP、系統調用邏輯等在Nginx層面做限流
    • 業務應用系統限流
      通過業務代碼控制流量這個流量可以被稱為信號量,可以理解成是一種鎖,它可以限制一項資源最多能同時被多少進程訪問。

      代碼實現

      import redis.clients.jedis.Jedis;
      import redis.clients.jedis.Transaction;
      import redis.clients.jedis.ZParams;

      import java.util.List;
      import java.util.UUID;

      /**
      * email wangiegie@gmail.com
      * @data 2017-08
      */
      public class RedisRateLimiter {
      private static final String BUCKET = "BUCKET";
      private static final String BUCKET_COUNT = "BUCKET_COUNT";
      private static final String BUCKET_MONITOR = "BUCKET_MONITOR";

      static String acquireTokenFromBucket(
      Jedis jedis, int limit, long timeout) {
      String identifier = UUID.randomUUID().toString();
      long now = System.currentTimeMillis();
      Transaction transaction = jedis.multi();

      //刪除信號量
      transaction.zremrangeByScore(BUCKET_MONITOR.getBytes(), "-inf".getBytes(), String.valueOf(now - timeout).getBytes());
      ZParams params = new ZParams();
      params.weightsByDouble(1.0,0.0);
      transaction.zinterstore(BUCKET, params, BUCKET, BUCKET_MONITOR);

      //計數器自增
      transaction.incr(BUCKET_COUNT);
      List<Object> results = transaction.exec();
      long counter = (Long) results.get(results.size() - 1);

      transaction = jedis.multi();
      transaction.zadd(BUCKET_MONITOR, now, identifier);
      transaction.zadd(BUCKET, counter, identifier);
      transaction.zrank(BUCKET, identifier);
      results = transaction.exec();
      //獲取排名,判斷請求是否取得了信號量
      long rank = (Long) results.get(results.size() - 1);
      if (rank < limit) {
      return identifier;
      } else {//沒有獲取到信號量,清理之前放入redis 中垃圾數據
      transaction = jedis.multi();
      transaction.zrem(BUCKET_MONITOR, identifier);
      transaction.zrem(BUCKET, identifier);
      transaction.exec();
      }
      return null;
      }
      }

    • 調用

       

       

      優化

      使用攔截器 + 注解優化代碼

      攔截器

       

       

      定義注解

       

       

      使用

       

       

      並發測試

      工具:apache-jmeter-3.2
      說明: 沒有獲取到信號量的接口返回500,status是紅色,獲取到信號量的接口返回200,status是綠色。
      當限制請求信號量為2,並發5個線程: image
      當限制請求信號量為5,並發10個線程:
      image

      資料

      基於reids + lua的實現

      總結

      1. 對於信號量的操作,使用事務操作。
      2. 不要使用時間戳作為信號量的排序分數,因為在分布式環境中,各個節點的時間差的原因,會出現不公平信號量的現象。
      3. 可以使用把這塊代碼抽成@rateLimiter注解,然后再方法上使用就會很方便啦
      4. 不同接口的流控,可以參考源碼的里面RedisRateLimiterPlus,無非是每個接口生成一個監控參數
      5. 源碼http://git.oschina.net/boding1/pig-cloud


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM