<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Guava RateLimiter 實(shí)現(xiàn) API 限流,這才是正確的姿勢(shì)!

          共 8876字,需瀏覽 18分鐘

           ·

          2021-10-16 07:54

          點(diǎn)擊關(guān)注公眾號(hào),回復(fù)“2T”獲取2TB學(xué)習(xí)資源!
          互聯(lián)網(wǎng)架構(gòu)師后臺(tái)回復(fù) 2T 有特別禮包
          上一篇:深夜看了張一鳴的微博,讓我越想越后怕


          Guava提供的RateLimiter可以限制物理或邏輯資源的被訪問速率,咋一聽有點(diǎn)像java并發(fā)包下的Samephore,但是又不相同,RateLimiter控制的是速率,Samephore控制的是并發(fā)量。

          RateLimiter的原理類似于令牌桶,它主要由許可發(fā)出的速率來定義,如果沒有額外的配置,許可證將按每秒許可證規(guī)定的固定速度分配,許可將被平滑地分發(fā),若請(qǐng)求超過permitsPerSecond則RateLimiter按照每秒 1/permitsPerSecond 的速率釋放許可。

          <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
             <version>23.0</version>
          </dependency>
          public static void main(String[] args) {
              String start = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
              RateLimiter limiter = RateLimiter.create(1.0); // 這里的1表示每秒允許處理的量為1個(gè)
              for (int i = 1; i <= 10; i++) { 
                  limiter.acquire();// 請(qǐng)求RateLimiter, 超過permits會(huì)被阻塞
                  System.out.println("call execute.." + i);
              }
              String end = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
              System.out.println("start time:" + start);
              System.out.println("end time:" + end);
          }

          可以看到,我假定了每秒處理請(qǐng)求的速率為1個(gè),現(xiàn)在我有10個(gè)任務(wù)要處理,那么RateLimiter就很好的實(shí)現(xiàn)了控制速率,總共10個(gè)任務(wù),需要9次獲取許可,所以最后10個(gè)任務(wù)的消耗時(shí)間為9s左右。那么在實(shí)際的項(xiàng)目中是如何使用的呢??

          另外,Java 多線程系列面試題和答案全部整理好了,微信搜索互聯(lián)網(wǎng)架構(gòu)師,在后臺(tái)發(fā)送:2T獲取。

          實(shí)際項(xiàng)目中使用

          @Service
          public class GuavaRateLimiterService {
              /*每秒控制5個(gè)許可*/
              RateLimiter rateLimiter = RateLimiter.create(5.0);
           
              /**
               * 獲取令牌
               *
               * @return
               */
              public boolean tryAcquire() {
                  return rateLimiter.tryAcquire();
              }
              
          }
            @Autowired
              private GuavaRateLimiterService rateLimiterService;
              
              @ResponseBody
              @RequestMapping("/ratelimiter")
              public Result testRateLimiter(){
                  if(rateLimiterService.tryAcquire()){
                      return ResultUtil.success1(1001,"成功獲取許可");
                  }
                  return ResultUtil.success1(1002,"未獲取到許可");
              }
          jmeter起10個(gè)線程并發(fā)訪問接口,測(cè)試結(jié)果如下:

          可以發(fā)現(xiàn),10個(gè)并發(fā)訪問總是只有6個(gè)能獲取到許可,結(jié)論就是能獲取到RateLimiter.create(n)中n+1個(gè)許可,總體來看Guava的RateLimiter是比較優(yōu)雅的。本文就是簡(jiǎn)單的提了下RateLimiter的使用。

          翻閱發(fā)現(xiàn)使用上述方式使用RateLimiter的方式不夠優(yōu)雅,盡管我們可以把RateLimiter的邏輯包在service里面,controller直接調(diào)用即可,但是如果我們換成:自定義注解+切面 的方式實(shí)現(xiàn)的話,會(huì)優(yōu)雅的多,詳細(xì)見下面代碼:

          自定義注解類

          import java.lang.annotation.*;
           
          /**
           * 自定義注解可以不包含屬性,成為一個(gè)標(biāo)識(shí)注解
           */
          @Inherited
          @Documented
          @Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          public @interface RateLimitAspect {
             
          }
          自定義切面類
          import com.google.common.util.concurrent.RateLimiter;
          import com.simons.cn.springbootdemo.util.ResultUtil;
          import net.sf.json.JSONObject;
          import org.aspectj.lang.ProceedingJoinPoint;
          import org.aspectj.lang.annotation.Around;
          import org.aspectj.lang.annotation.Aspect;
          import org.aspectj.lang.annotation.Pointcut;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.context.annotation.Scope;
          import org.springframework.stereotype.Component;
           
          import javax.servlet.ServletOutputStream;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;
           
          @Component
          @Scope
          @Aspect
          public class RateLimitAop {
           
              @Autowired
              private HttpServletResponse response;
           
              private RateLimiter rateLimiter = RateLimiter.create(5.0); //比如說,我這里設(shè)置"并發(fā)數(shù)"為5
           
              @Pointcut("@annotation(com.simons.cn.springbootdemo.aspect.RateLimitAspect)")
              public void serviceLimit() {
           
              }
           
              @Around("serviceLimit()")
              public Object around(ProceedingJoinPoint joinPoint) {
                  Boolean flag = rateLimiter.tryAcquire();
                  Object obj = null;
                  try {
                      if (flag) {
                          obj = joinPoint.proceed();
                      }else{
                          String result = JSONObject.fromObject(ResultUtil.success1(100, "failure")).toString();
                          output(response, result);
                      }
                  } catch (Throwable e) {
                      e.printStackTrace();
                  }
                  System.out.println("flag=" + flag + ",obj=" + obj);
                  return obj;
              }
              
              public void output(HttpServletResponse response, String msg) throws IOException {
                  response.setContentType("application/json;charset=UTF-8");
                  ServletOutputStream outputStream = null;
                  try {
                      outputStream = response.getOutputStream();
                      outputStream.write(msg.getBytes("UTF-8"));
                  } catch (IOException e) {
                      e.printStackTrace();
                  } finally {
                      outputStream.flush();
                      outputStream.close();
                  }
              }
          }
          測(cè)試controller類
          import com.simons.cn.springbootdemo.aspect.RateLimitAspect;
          import com.simons.cn.springbootdemo.util.ResultUtil;
          import org.springframework.stereotype.Controller;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.ResponseBody;
           
          /**
           * 類描述:RateLimit限流測(cè)試(基于 注解+切面 方式)
           * 創(chuàng)建人:simonsfan
           */
          @Controller
          public class TestController {
           
              @ResponseBody
              @RateLimitAspect         //可以非常方便的通過這個(gè)注解來實(shí)現(xiàn)限流
              @RequestMapping("/test")
              public String test(){
                  return ResultUtil.success1(1001, "success").toString();
              }
          這樣通過自定義注解@RateLimiterAspect來動(dòng)態(tài)的加到需要限流的接口上,個(gè)人認(rèn)為是比較優(yōu)雅的實(shí)現(xiàn)吧。

          壓測(cè)結(jié)果:

          可以看到,10個(gè)線程中無論壓測(cè)多少次,并發(fā)數(shù)總是限制在6,也就實(shí)現(xiàn)了限流。

          作者:飯一碗
          來源:https://blog.csdn.net/fanrenxiang/article/details/80949079

          感謝您的閱讀,也歡迎您發(fā)表關(guān)于這篇文章的任何建議,關(guān)注我,技術(shù)不迷茫!小編到你上高速。
              · END ·
          最后,關(guān)注公眾號(hào)互聯(lián)網(wǎng)架構(gòu)師,在后臺(tái)回復(fù):2T,可以獲取我整理的 Java 系列面試題和答案,非常齊全。


          正文結(jié)束


          推薦閱讀 ↓↓↓

          1.不認(rèn)命,從10年流水線工人,到谷歌上班的程序媛,一位湖南妹子的勵(lì)志故事

          2.如何才能成為優(yōu)秀的架構(gòu)師?

          3.從零開始搭建創(chuàng)業(yè)公司后臺(tái)技術(shù)棧

          4.程序員一般可以從什么平臺(tái)接私活?

          5.37歲程序員被裁,120天沒找到工作,無奈去小公司,結(jié)果懵了...

          6.IntelliJ IDEA 2019.3 首個(gè)最新訪問版本發(fā)布,新特性搶先看

          7.這封“領(lǐng)導(dǎo)痛批95后下屬”的郵件,句句扎心!

          8.15張圖看懂瞎忙和高效的區(qū)別!

          一個(gè)人學(xué)習(xí)、工作很迷茫?


          點(diǎn)擊「閱讀原文」加入我們的小圈子!

          瀏覽 63
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩无码十八禁 | 亚洲欧洲日本国产 | 日逼网站123 | 在线日韩一区二区 | 91精品国产91久久久久游泳池 |