国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > Java > 正文

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

2024-07-13 10:14:44
字體:
來源:轉載
供稿:網友

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

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

優化

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

攔截器

@Configurationstatic 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)@Documentedpublic @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,分布式,限流,實現限流,redis實現分布式限流

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

Redis,分布式,限流,實現限流,redis實現分布式限流

資料

基于reids + lua的實現

總結

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

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


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 金平| 海门市| 四平市| 钦州市| 永丰县| 尉氏县| 原平市| 安平县| 南漳县| 长春市| 庆云县| 米易县| 巨鹿县| 鸡泽县| 广西| 司法| 隆林| 灵川县| 麦盖提县| 西乡县| 开江县| 泸定县| 大冶市| 卓尼县| 牡丹江市| 秀山| 贵南县| 余江县| 攀枝花市| 清徐县| 五家渠市| 专栏| 岳阳县| 闻喜县| 台江县| 阿巴嘎旗| 青神县| 赤城县| 张家界市| 七台河市| 和顺县|