最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot使用自定義注解+AOP+Redis實(shí)現(xiàn)接口限流的實(shí)例代碼

 更新時(shí)間:2022年09月07日 11:51:04   作者:Jae1995  
這篇文章主要介紹了SpringBoot使用自定義注解+AOP+Redis實(shí)現(xiàn)接口限流,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

為什么要限流

系統(tǒng)在設(shè)計(jì)的時(shí)候,我們會(huì)有一個(gè)系統(tǒng)的預(yù)估容量,長時(shí)間超過系統(tǒng)能承受的TPS/QPS閾值,系統(tǒng)有可能會(huì)被壓垮,最終導(dǎo)致整個(gè)服務(wù)不可用。為了避免這種情況,我們就需要對接口請求進(jìn)行限流。

所以,我們可以通過對并發(fā)訪問請求進(jìn)行限速或者一個(gè)時(shí)間窗口內(nèi)的的請求數(shù)量進(jìn)行限速來保護(hù)系統(tǒng)或避免不必要的資源浪費(fèi),一旦達(dá)到限制速率則可以拒絕服務(wù)、排隊(duì)或等待。

限流背景

系統(tǒng)有一個(gè)獲取手機(jī)短信驗(yàn)證碼的接口,因?yàn)槭情_放接口,所以為了避免用戶不斷的發(fā)送請求獲取驗(yàn)證碼,防止惡意刷接口的情況發(fā)生,于是用最簡單的計(jì)數(shù)器方式做了限流,限制每個(gè)IP每分鐘只能請求一次,然后其他每個(gè)手機(jī)號(hào)的時(shí)間窗口限制則是通過業(yè)務(wù)邏輯進(jìn)行判斷。一般一些接口訪問量比較大的,可能會(huì)壓垮系統(tǒng)的,則需要加入流量限制!如:秒殺等...

實(shí)現(xiàn)限流

1、引入依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>       

2、自定義限流注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter
{
    /**
     * 限流key
     */
     String key() default Constants.RATE_LIMIT_KEY;

    /**
     * 限流時(shí)間,單位秒
     */
     int time() default 60;

    /**
     * 限流次數(shù)
     */
    int count() default 100;

    /**
     * 限流類型
     */
    LimitType limitType() default LimitType.DEFAULT;

    /**
     * 限流后返回的文字
     */
    String limitMsg() default "訪問過于頻繁,請稍候再試";
}

3、限流切面

@Aspect
@Component
public class RateLimiterAspect {

    private final static Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);

    @Autowired
    private RedisUtils redisUtils;

    @Before("@annotation(rateLimiter)")
    public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable
    {
        int time = rateLimiter.time();
        int count = rateLimiter.count();
        long total = 1L;

        String combineKey = getCombineKey(rateLimiter, point);
        try
        {
            if(redisUtils.hasKey(combineKey)){
                total = redisUtils.incr(combineKey,1);  //請求進(jìn)來,對應(yīng)的key加1
                if(total > count)
                    throw new ServiceRuntimeException(rateLimiter.limitMsg());
            }else{
                redisUtils.set(combineKey,1,time);  //初始化key
            }
        }
        catch (ServiceRuntimeException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ServiceRuntimeException("網(wǎng)絡(luò)繁忙,請稍候再試");
        }
    }

    /**
     * 獲取限流key
     * @param rateLimiter
     * @param point
     * @return
     */
    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
    {
        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
        if (rateLimiter.limitType() == LimitType.IP)
        {
            stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
        return stringBuffer.toString();
    }
}

4、寫一個(gè)簡單的接口進(jìn)行測試

@RestController
public class TestController {

    @RateLimiter(time = 60, count = 1, limitType = LimitType.IP, limitMsg = "一分鐘內(nèi)只能請求一次,請稍后重試")
    @GetMapping("/hello")
    public ResultMsg hello() {
        return ResultMsg.success("Hello World!");
    }
}

5、全局異常攔截

@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 業(yè)務(wù)異常
     */
    @ExceptionHandler(ServiceRuntimeException.class)
    public ResultMsg handleServiceException(ServiceRuntimeException e, HttpServletRequest request)
    {
        return ResultMsg.error(e.getMessage());
    }

    /**
     * 系統(tǒng)異常
     */
    @ExceptionHandler(Exception.class)
    public ResultMsg handleException(Exception e, HttpServletRequest request)
    {
        return ResultMsg.error("系統(tǒng)異常");
    }
}

6、接口測試

1)第一次發(fā)送,正常返回結(jié)果

2)一分鐘內(nèi)第二次發(fā)送,返回錯(cuò)誤,限流提示

好了,大功告成啦

還有其他的限流方式,如滑動(dòng)窗口限流方式(比計(jì)數(shù)器更嚴(yán)謹(jǐn))、令牌桶等...,有興趣的小伙伴可以學(xué)習(xí)一下

附源碼

https://gitee.com/jae_1995/ratelimiter

到此這篇關(guān)于SpringBoot使用自定義注解+AOP+Redis實(shí)現(xiàn)接口限流的文章就介紹到這了,更多相關(guān)SpringBoot接口限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringMVC攔截器快速掌握上篇

    SpringMVC攔截器快速掌握上篇

    攔截器(Interceptor)是一種動(dòng)態(tài)攔截方法調(diào)用的機(jī)制,在SpringMVC中動(dòng)態(tài)攔截控制器方法的執(zhí)行。本文將詳細(xì)講講SpringMVC中攔截器的概念及入門案例,感興趣的可以嘗試一下
    2022-08-08
  • Spring Boot2集成AOPLog來記錄接口訪問日志

    Spring Boot2集成AOPLog來記錄接口訪問日志

    這篇文章主要介紹了Spring Boot2集成AOPLog來記錄接口訪問日志,日志是一個(gè)Web項(xiàng)目中必不可少的部分,借助它我們可以做許多事情,比如問題排查、訪問統(tǒng)計(jì)、監(jiān)控告警等,需要的朋友可以參考下
    2019-06-06
  • Java中雙冒號(hào)::的作用舉例詳解

    Java中雙冒號(hào)::的作用舉例詳解

    這篇文章主要給大家介紹了關(guān)于Java中雙冒號(hào)::作用的相關(guān)資料,雙冒號(hào)(::)運(yùn)算符在Java?8中被用作方法引用(method?reference),方法引用是與lambda表達(dá)式相關(guān)的一個(gè)重要特性,需要的朋友可以參考下
    2023-11-11
  • Mybatis-Plus或PageHelper多表分頁查詢總條數(shù)不對問題的解決方法

    Mybatis-Plus或PageHelper多表分頁查詢總條數(shù)不對問題的解決方法

    PageHelper 這個(gè)插件用了很多次了,今天使用的時(shí)候才遇到一個(gè)問題,這篇文章主要給大家介紹了關(guān)于Mybatis-Plus或PageHelper多表分頁查詢總條數(shù)不對問題的解決方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • java+selenium實(shí)現(xiàn)滑塊驗(yàn)證

    java+selenium實(shí)現(xiàn)滑塊驗(yàn)證

    現(xiàn)在越來越多的網(wǎng)站都使用采用滑塊驗(yàn)證來作為驗(yàn)證機(jī)制,用于判斷用戶是否為人類而不是機(jī)器人,本文就將利用java和selenium實(shí)現(xiàn)滑塊驗(yàn)證,希望對大家有所幫助
    2023-12-12
  • springmvc和js前端的數(shù)據(jù)傳遞和接收方式(兩種)

    springmvc和js前端的數(shù)據(jù)傳遞和接收方式(兩種)

    本文介紹了springmvc和js前端的數(shù)據(jù)傳遞和接收方式(兩種),詳細(xì)的介紹了兩種方式,一種是json格式傳遞,另一種是Map傳遞,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-12-12
  • Java線程池內(nèi)部任務(wù)出異常后如何知道是哪個(gè)線程出了異常

    Java線程池內(nèi)部任務(wù)出異常后如何知道是哪個(gè)線程出了異常

    在Java線程池中當(dāng)任務(wù)執(zhí)行過程中發(fā)生異常時(shí),識(shí)別發(fā)生異常的任務(wù)所對應(yīng)的線程是一個(gè)常見的調(diào)試需求,這篇文章主要介紹了Java線程池內(nèi)部任務(wù)出異常后如何知道是哪個(gè)線程出了異常的相關(guān)資料,需要的朋友可以參考下
    2026-01-01
  • 淺析Java中的繼承與組合

    淺析Java中的繼承與組合

    本文將介紹組合和繼承的概念及區(qū)別,并從多方面分析在寫代碼時(shí)如何進(jìn)行選擇。文中通過示例代碼介紹的很詳細(xì),有需要的朋友可以參考借鑒,下面來一起看看吧。
    2016-12-12
  • IDEA多線程文件下載插件開發(fā)的步驟詳解

    IDEA多線程文件下載插件開發(fā)的步驟詳解

    這篇文章主要介紹了IDEA多線程文件下載插件開發(fā)的步驟詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • SpringBoot使用注解集成Redis緩存的示例代碼

    SpringBoot使用注解集成Redis緩存的示例代碼

    這篇文章主要介紹了在?Spring?Boot?中使用注解集成?Redis?緩存的步驟,包括添加依賴、創(chuàng)建相關(guān)配置類、需要緩存數(shù)據(jù)的類(TestService)以及測試方法(如在?Controller?中的?redisTest?和?redisCache?方法),還對一些關(guān)鍵代碼和配置進(jìn)行了詳細(xì)說明
    2025-01-01

最新評(píng)論

澄江县| 社旗县| 肥东县| 北宁市| 象州县| 江口县| 色达县| 雷波县| 乐陵市| 德州市| 莱阳市| 莲花县| 东平县| 玉山县| 商河县| 尚义县| 旺苍县| 罗甸县| 龙口市| 隆德县| 勐海县| 洪江市| 卢龙县| 镇原县| 开江县| 交口县| 元谋县| 同江市| 长沙县| 阿鲁科尔沁旗| 渑池县| 仙居县| 黄陵县| 思南县| 稷山县| 丰原市| 东源县| 呼伦贝尔市| 建昌县| 泰州市| 额敏县|