91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

基于Redis實現分布式應用限流的方法

發布時間:2020-09-24 10:22:35 來源:腳本之家 閱讀:169 作者:冷冷gg 欄目:編程語言

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

前幾天在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;
  }
}

調用

測試接口調用

@GetMapping("/")
public void index(HttpServletResponse response) throws IOException {
  Jedis jedis = jedisPool.getResource();
  String token = RedisRateLimiter.acquireTokenFromBucket(jedis, LIMIT, TIMEOUT);
  if (token == null) {
    response.sendError(500);
  }else{
    //TODO 你的業務邏輯
  }
  jedisPool.returnResource(jedis);
}

優化

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

攔截器

@Configuration
static class WebMvcConfigurer extends WebMvcConfigurerAdapter {
  private Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);
  @Autowired
  private JedisPool jedisPool;

  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new HandlerInterceptorAdapter() {
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                   Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class);
        if (rateLimiter != null){
          int limit = rateLimiter.limit();
          int timeout = rateLimiter.timeout();
          Jedis jedis = jedisPool.getResource();
          String token = RedisRateLimiter.acquireTokenFromBucket(jedis, limit, timeout);
          if (token == null) {
            response.sendError(500);
            return false;
          }
          logger.debug("token -> {}",token);
          jedis.close();
        }
        return true;
      }
    }).addPathPatterns("/*");
  }
}

定義注解

/**
 * @email wangiegie@gmail.com
 * @data 2017-08
 * 限流注解
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
  int limit() default 5;
  int timeout() default 1000;
}

使用

@RateLimiter(limit = 2, timeout = 5000)
@GetMapping("/test")
public void test() {
}

并發測試

工具:apache-jmeter-3.2

說明: 沒有獲取到信號量的接口返回500,status是紅色,獲取到信號量的接口返回200,status是綠色。

當限制請求信號量為2,并發5個線程:

基于Redis實現分布式應用限流的方法

當限制請求信號量為5,并發10個線程:

基于Redis實現分布式應用限流的方法

資料

基于reids + lua的實現

總結

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

静乐县| 兴安盟| 临颍县| 永吉县| 桑日县| 灌云县| 海伦市| 广平县| 新河县| 大邑县| 禹州市| 石景山区| 密云县| 长沙市| SHOW| 鄄城县| 乡城县| 化隆| 龙门县| 五台县| 西平县| 丘北县| 泾源县| 岱山县| 诏安县| 泸西县| 台山市| 夏邑县| 舒城县| 营口市| 桦川县| 麦盖提县| 舟曲县| 芦山县| 姚安县| 建湖县| 交城县| 外汇| 云南省| 台安县| 阿勒泰市|