成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

基于Redis實(shí)現(xiàn)分布式應(yīng)用限流的方法

限流的目的是通過對(duì)并發(fā)訪問/請(qǐng)求進(jìn)行限速或者一個(gè)時(shí)間窗口內(nèi)的的請(qǐng)求進(jìn)行限速來保護(hù)系統(tǒng),一旦達(dá)到限制速率則可以拒絕服務(wù)。

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、微信小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了滎經(jīng)免費(fèi)建站歡迎大家使用!

前幾天在DD的公眾號(hào),看了一篇關(guān)于使用 瓜娃 實(shí)現(xiàn)單應(yīng)用限流的方案 --》原文,參考《redis in action》 實(shí)現(xiàn)了一個(gè)jedis版本的,都屬于業(yè)務(wù)層次限制。 實(shí)際場景中常用的限流策略:

Nginx接入層限流

按照一定的規(guī)則如帳號(hào)、IP、系統(tǒng)調(diào)用邏輯等在Nginx層面做限流

業(yè)務(wù)應(yīng)用系統(tǒng)限流

通過業(yè)務(wù)代碼控制流量這個(gè)流量可以被稱為信號(hào)量,可以理解成是一種鎖,它可以限制一項(xiàng)資源最多能同時(shí)被多少進(jìn)程訪問。

代碼實(shí)現(xiàn)

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();

    //刪除信號(hào)量
    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);

    //計(jì)數(shù)器自增
    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();
    //獲取排名,判斷請(qǐng)求是否取得了信號(hào)量
    long rank = (Long) results.get(results.size() - 1);
    if (rank < limit) {
      return identifier;
    } else {//沒有獲取到信號(hào)量,清理之前放入redis 中垃圾數(shù)據(jù)
      transaction = jedis.multi();
      transaction.zrem(BUCKET_MONITOR, identifier);
      transaction.zrem(BUCKET, identifier);
      transaction.exec();
    }
    return null;
  }
}

調(diào)用

測(cè)試接口調(diào)用

@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 你的業(yè)務(wù)邏輯
  }
  jedisPool.returnResource(jedis);
}

優(yōu)化

使用攔截器 + 注解優(yōu)化代碼

攔截器

@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() {
}

并發(fā)測(cè)試

工具:apache-jmeter-3.2

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

當(dāng)限制請(qǐng)求信號(hào)量為2,并發(fā)5個(gè)線程:

基于Redis實(shí)現(xiàn)分布式應(yīng)用限流的方法

當(dāng)限制請(qǐng)求信號(hào)量為5,并發(fā)10個(gè)線程:

基于Redis實(shí)現(xiàn)分布式應(yīng)用限流的方法

資料

基于reids + lua的實(shí)現(xiàn)

總結(jié)

  1. 對(duì)于信號(hào)量的操作,使用事務(wù)操作。
  2. 不要使用時(shí)間戳作為信號(hào)量的排序分?jǐn)?shù),因?yàn)樵诜植际江h(huán)境中,各個(gè)節(jié)點(diǎn)的時(shí)間差的原因,會(huì)出現(xiàn)不公平信號(hào)量的現(xiàn)象。
  3. 可以使用把這塊代碼抽成@rateLimiter注解,然后再方法上使用就會(huì)很方便啦
  4. 不同接口的流控,可以參考源碼的里面RedisRateLimiterPlus,無非是每個(gè)接口生成一個(gè)監(jiān)控參數(shù)
  5. 源碼:boding1-pig-cloud-jb51.rar

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

當(dāng)前文章:基于Redis實(shí)現(xiàn)分布式應(yīng)用限流的方法
URL分享:http://jinyejixie.com/article34/iisjpe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、網(wǎng)站維護(hù)、響應(yīng)式網(wǎng)站商城網(wǎng)站、手機(jī)網(wǎng)站建設(shè)、虛擬主機(jī)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司
大兴区| 莎车县| 锦屏县| 黑山县| 林口县| 和平区| 伽师县| 桂林市| 奉新县| 长丰县| 敦化市| 金山区| 鹤庆县| 云安县| 河北省| 吉首市| 乐东| 武安市| 凤山县| 五大连池市| 连城县| 大田县| 林州市| 大理市| 阳高县| 徐州市| 长丰县| 兰溪市| 昌都县| 会理县| 军事| 洪洞县| 营山县| 正安县| 巴中市| 临沧市| 龙山县| 朝阳市| 朝阳区| 忻城县| 肇源县|