<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 實現(xiàn) API 限流,這才是正確的姿勢!

          共 618字,需瀏覽 2分鐘

           ·

          2021-10-17 10:11

          點擊關注公眾號,Java干貨及時送達

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

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


          ???com.google.guava
          ???guava
          ???23.0

          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個
          ????for?(int?i?=?1;?i?<=?10;?i++)?{?
          ????????limiter.acquire();//?請求RateLimiter,?超過permits會被阻塞
          ????????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);
          }

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

          另外,Java 多線程系列面試題和答案全部整理好了,微信搜索Java技術棧,在后臺發(fā)送:面試,可以在線閱讀。

          實際項目中使用

          @Service
          public?class?GuavaRateLimiterService?{
          ????/*每秒控制5個許可*/
          ????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個線程并發(fā)訪問接口,測試結果如下:

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

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

          自定義注解類

          import?java.lang.annotation.*;
          ?
          /**
          ?*?自定義注解可以不包含屬性,成為一個標識注解
          ?*/
          @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);?//比如說,我這里設置"并發(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();
          ????????}
          ????}
          }

          測試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限流測試(基于?注解+切面?方式)
          ?*?創(chuàng)建人:simonsfan
          ?*/
          @Controller
          public?class?TestController?{
          ?
          ????@ResponseBody
          ????@RateLimitAspect?????????//可以非常方便的通過這個注解來實現(xiàn)限流
          ????@RequestMapping("/test")
          ????public?String?test(){
          ????????return?ResultUtil.success1(1001,?"success").toString();
          ????}

          這樣通過自定義注解@RateLimiterAspect來動態(tài)的加到需要限流的接口上,個人認為是比較優(yōu)雅的實現(xiàn)吧。推薦一個 Spring Boot 基礎教程及實戰(zhàn)示例:https://www.javastack.cn/categories/Spring-Boot/

          壓測結果:

          可以看到,10個線程中無論壓測多少次,并發(fā)數(shù)總是限制在6,也就實現(xiàn)了限流。另外,關注公眾號Java技術棧,在后臺回復:面試,可以獲取我整理的 Java 多線程系列面試題和答案,非常齊全。

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





          關注Java技術??锤喔韶?/strong>



          獲取 Spring Boot 實戰(zhàn)筆記!
          瀏覽 121
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  日韩性爱人人操 | 亚洲色情直播 | www.91AV在线免费观看 | 青娱乐亚洲高清视频 | 欧美海量操逼 |