亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

服務(wù)限流(自定義注解令牌桶算法)

microcosm1994 / 2781人閱讀

摘要:自定義注解實現(xiàn)基于接口限流仔細(xì)看會發(fā)現(xiàn)上面的簡單實現(xiàn)會造成我每個接口都要寫一次限流方法代碼很冗余所以采用來使用自定義注解來實現(xiàn)。

服務(wù)限流 -- 自定義注解基于RateLimiter實現(xiàn)接口限流

令牌桶限流算法

圖片來自網(wǎng)上

令牌桶會以一個恒定的速率向固定容量大小桶中放入令牌,當(dāng)有瀏覽來時取走一個或者多個令牌,當(dāng)發(fā)生高并發(fā)情況下拿到令牌的執(zhí)行業(yè)務(wù)邏輯,沒有獲取到令牌的就會丟棄獲取服務(wù)降級處理,提示一個友好的錯誤信息給用戶。

2. RateLimiter簡單實現(xiàn)
maven依賴

        
        
            com.google.guava
            guava
            18.0
        

本人使用的是SpringBoot 2.0.4.RELEASE,Jdk1.8環(huán)境下編寫,部分代碼貼出:

   /**
     * 以1r/s往桶中放入令牌
     */
    private RateLimiter limiter = RateLimiter.create(1.0);

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @GetMapping("/indexLimiter")
    public String indexLimiter() {
        // 如果用戶在500毫秒內(nèi)沒有獲取到令牌,就直接放棄獲取進行服務(wù)降級處理
        boolean tryAcquire = limiter.tryAcquire(500, TimeUnit.MILLISECONDS);
        if (!tryAcquire) {
            log.info("Error ---時間:{},獲取令牌失敗.", sdf.format(new Date()));
            return "系統(tǒng)繁忙,請稍后再試.";
        }
        log.info("Success ---時間:{},獲取令牌成功.", sdf.format(new Date()));
        return "success";
    }

調(diào)用結(jié)果如下:

使用RateLimiter注意的地方:

允許先消費,后付款,意思就是它可以來一個請求的時候一次性取走幾個或者是剩下所有的令牌甚至多取,但是后面的請求就得為上一次請求買單,它需要等待桶中的令牌補齊之后才能繼續(xù)獲取令牌。

3.自定義注解實現(xiàn)基于接口限流

仔細(xì)看會發(fā)現(xiàn)上面的簡單實現(xiàn)會造成我每個接口都要寫一次限流方法代碼很冗余,所以采用aop來使用自定義注解來實現(xiàn)。

maven依賴

        
        
            org.springframework.boot
            spring-boot-starter-aop
        
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            com.google.guava
            guava
            18.0
        
        
        
            org.projectlombok
            lombok
            true
        

首先定義一個自定義注解:

package com.limiting.annotation;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface AnRateLimiter {

    //以固定數(shù)值往令牌桶添加令牌
    double permitsPerSecond () ;

    //獲取令牌最大等待時間
    long timeout();

    // 單位(例:分鐘/秒/毫秒) 默認(rèn):毫秒
    TimeUnit timeunit() default TimeUnit.MILLISECONDS;

    // 無法獲取令牌返回提示信息 默認(rèn)值可以自行修改
    String msg() default "系統(tǒng)繁忙,請稍后再試.";
}

然后使用aop的環(huán)繞通知來攔截注解,使用了一個ConcurrentMap來保存每個請求對應(yīng)的令牌桶,key是沒有url請求,防止出現(xiàn)每個請求都會新建一個令牌桶這么會達不到限流效果.

package com.limiting.aspect;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import com.limiting.annotation.AnRateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
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.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

/**
 *  * 描述:
 *
 * @author 只寫B(tài)UG的攻城獅
 *  * @date 2018-09-12 12:07
 */
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {
    /**
     * 使用url做為key,存放令牌桶 防止每次重新創(chuàng)建令牌桶
     */
    private Map limitMap = Maps.newConcurrentMap();
    
    @Pointcut("@annotation(com.limiting.annotation.AnRateLimiter)")
    public void anRateLimiter() {
    }

    @Around("anRateLimiter()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 獲取request,response
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        // 或者url(存在map集合的key)
        String url = request.getRequestURI();
        // 獲取自定義注解
        AnRateLimiter rateLimiter = getAnRateLimiter(joinPoint);
        if (rateLimiter != null) {
            RateLimiter limiter = null;
            // 判斷map集合中是否有創(chuàng)建有創(chuàng)建好的令牌桶
            if (!limitMap.containsKey(url)) {
                // 創(chuàng)建令牌桶
                limiter = RateLimiter.create(rateLimiter.permitsPerSecond());
                limitMap.put(url, limiter);
                log.info("<<=================  請求{},創(chuàng)建令牌桶,容量{} 成功!!!", url, rateLimiter.permitsPerSecond());
            }
            limiter = limitMap.get(url);
            // 獲取令牌
            boolean acquire = limiter.tryAcquire(rateLimiter.timeout(), rateLimiter.timeunit());

            if (!acquire) {
                responseResult(response, 500, rateLimiter.msg());
                return null;
            }
        }
        return joinPoint.proceed();
    }

    /**
     * 獲取注解對象
     * @param joinPoint 對象
     * @return ten LogAnnotation
     */
    private AnRateLimiter getAnRateLimiter(final JoinPoint joinPoint) {
        Method[] methods = joinPoint.getTarget().getClass().getDeclaredMethods();
        String name = joinPoint.getSignature().getName();
        if (!StringUtils.isEmpty(name)) {
            for (Method method : methods) {
                AnRateLimiter annotation = method.getAnnotation(AnRateLimiter.class);
                if (!Objects.isNull(annotation) && name.equals(method.getName())) {
                    return annotation;
                }
            }
        }
        return null;
    }

    /**
     * 自定義響應(yīng)結(jié)果
     *
     * @param response 響應(yīng)
     * @param code     響應(yīng)碼
     * @param message  響應(yīng)信息
     */
    private void responseResult(HttpServletResponse response, Integer code, String message) {
        response.resetBuffer();
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.println("{"code":" + code + " ,"message" :"" + message + ""}");
            response.flushBuffer();
        } catch (IOException e) {
            log.error(" 輸入響應(yīng)出錯 e = {}", e.getMessage(), e);
        } finally {
            if (writer != null) {
                writer.flush();
                writer.close();
            }
        }
    }
}

最后來試試自己定義的注解是否生效,能否達到限流效果.

    @GetMapping("/index")
    @AnRateLimiter(permitsPerSecond = 1, timeout = 500, timeunit = TimeUnit.MILLISECONDS,msg = "親,現(xiàn)在流量過大,請稍后再試.")
    public String index() {
        return System.currentTimeMillis() + "";
    }

訪問請求(按F5狂刷新瀏覽器)效果如下圖:

總結(jié)
至此已基本上使用注解實現(xiàn)了接口限流,后期可以根據(jù)自己需求自行修改,這個只適于單個應(yīng)用進行接口限流,如果是分布式項目或者微服務(wù)項目可以采用redis來實現(xiàn),后期有時間來一個基于redis自定義注解來實現(xiàn)接口限流。
本人也是剛?cè)隞ava開發(fā)行業(yè)沒多久的小菜鳥,在文章中可能存在一些說的不對,代碼不嚴(yán)謹(jǐn)?shù)牡胤綒g迎各位大神指出,本人表示由衷的感謝和耐心的學(xué)習(xí),希望能在開發(fā)中給大家一些幫助。

參考
https://crossoverjie.top/2017...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/77105.html

相關(guān)文章

  • 幾種限流技術(shù)

    摘要:下面是幾種常見的限流技術(shù)一限流算法常用的限流算法有令牌桶,漏桶令牌桶令牌桶算法是網(wǎng)絡(luò)流量整形和速率限制中最常使用的一種算法。 就秒殺接口來說,當(dāng)訪問頻率或者并發(fā)請求超過其承受范圍的時候,這時候我們就要考慮限流來保證接口的可用性,以防止非預(yù)期的請求對系統(tǒng)壓力過大而引起的系統(tǒng)癱瘓。通常的策略就是拒絕多余的訪問,或者讓多余的訪問排隊等待服務(wù)。下面是幾種常見的限流技術(shù) 一、限流算法常用的限流算...

    Warren 評論0 收藏0
  • 限流器及Guava實現(xiàn)分析

    摘要:計數(shù)限流算法無論固定窗口還是滑動窗口核心均是對請求進行計數(shù),區(qū)別僅僅在于對于計數(shù)時間區(qū)間的處理。令牌桶限流實現(xiàn)原理令牌桶限流的實現(xiàn)原理在有詳細(xì)說明。因此由此為入口進行分析。目前可返回的實現(xiàn)子類包括及兩種,具體不同下文詳細(xì)分析。 限流 限流一詞常用于計算機網(wǎng)絡(luò)之中,定義如下: In computer networks, rate limiting is used to control t...

    xcc3641 評論0 收藏0
  • 接口限流算法:漏算法&令牌算法

    摘要:令牌桶算法漏桶算法漏桶漏桶的出水速度是恒定的,那么意味著如果瞬時大流量的話,將有大部分請求被丟棄掉也就是所謂的溢出。 工作中對外提供的API 接口設(shè)計都要考慮限流,如果不考慮限流,會成系統(tǒng)的連鎖反應(yīng),輕者響應(yīng)緩慢,重者系統(tǒng)宕機,整個業(yè)務(wù)線崩潰,如何應(yīng)對這種情況呢,我們可以對請求進行引流或者直接拒絕等操作,保持系統(tǒng)的可用性和穩(wěn)定性,防止因流量暴增而導(dǎo)致的系統(tǒng)運行緩慢或宕機。 在開發(fā)高并發(fā)...

    dendoink 評論0 收藏0
  • [登錄那些事] 郵件發(fā)送,限流,漏令牌

    摘要:關(guān)于如何限速,有兩個比較出名的算法,漏桶算法與令牌桶算法,這里對其簡單介紹一下,最后再實踐在我發(fā)郵件的中以下是發(fā)送郵件的,已限制為一分鐘兩次,你可以通過修改進行試驗。 前段時間,我使用了 jwt 來實現(xiàn)郵箱驗證碼的校驗與用戶認(rèn)證與登錄,還特別寫了一篇文章作為總結(jié)。 在那篇文章中,提到了一個點,如何限速。 在短信驗證碼和郵箱驗證碼,如果不限速,被惡意攻擊造成大量的 QPS,不僅拖垮了服務(wù)...

    wpw 評論0 收藏0

發(fā)表評論

0條評論

microcosm1994

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<