<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

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

    共 618字,需瀏覽 2分鐘

     ·

    2021-10-16 20:28

    點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

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

    RateLimiter的原理類似于令牌桶,它主要由許可發(fā)出的速率來定義,如果沒有額外的配置,許可證將按每秒許可證規(guī)定的固定速度分配,許可將被平滑地分發(fā),若請(qǐng)求超過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個(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 多線程系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺(tái)發(fā)送:面試,可以在線閱讀。

    實(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ā)訪問接口,測試結(jié)果如下:

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

    測試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?????????//可以非常方便的通過這個(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)吧。推薦一個(gè) Spring Boot 基礎(chǔ)教程及實(shí)戰(zhàn)示例:https://www.javastack.cn/categories/Spring-Boot/

    壓測結(jié)果:

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

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


    1、靈魂一問:你的登錄接口真的安全嗎?

    2、HashMap 中這些設(shè)計(jì),絕了~

    3、在 IntelliJ IDEA 中這樣使用 Git,賊方便了!

    4、計(jì)算機(jī)時(shí)間到底是怎么來的?程序員必看的時(shí)間知識(shí)!

    5、這些IDEA的優(yōu)化設(shè)置趕緊安排起來,效率提升杠杠的!

    6、21 款 yyds 的 IDEA插件

    7、真香!用 IDEA 神器看源碼,效率真高!

    點(diǎn)分享

    點(diǎn)收藏

    點(diǎn)點(diǎn)贊

    點(diǎn)在看

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

    手機(jī)掃一掃分享

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

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    俺来也图片区视频区 | 男女操逼免费看 | 91久久免费 | 无码一区二区三区四区五区六区七区 | 操你啦人人操 |