欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java AOP實(shí)現(xiàn)自定義滑動(dòng)窗口限流器方法詳解

 更新時(shí)間:2022年07月18日 15:36:15   作者:柚幾哥哥  
這篇文章主要介紹了Java AOP實(shí)現(xiàn)自定義滑動(dòng)窗口限流器方法,其中滑動(dòng)窗口算法彌補(bǔ)了計(jì)數(shù)器算法的不足,滑動(dòng)窗口算法把間隔時(shí)間劃分成更小的粒度,當(dāng)更小粒度的時(shí)間間隔過(guò)去后,把過(guò)去的間隔請(qǐng)求數(shù)減掉,再補(bǔ)充一個(gè)空的時(shí)間間隔,需要的朋友可以參考下

滑動(dòng)窗口算法

滑動(dòng)窗口算法是一種廣泛應(yīng)用于計(jì)算機(jī)科學(xué)和數(shù)據(jù)分析中的數(shù)據(jù)流算法,特別適用于處理具有時(shí)間序列特性的數(shù)據(jù),如網(wǎng)絡(luò)流量監(jiān)控、速率限制、數(shù)據(jù)分析等領(lǐng)域。其核心思想是在一個(gè)固定大小的“窗口”內(nèi)對(duì)數(shù)據(jù)進(jìn)行統(tǒng)計(jì)分析,這個(gè)窗口會(huì)隨著數(shù)據(jù)的流入而向前滑動(dòng),始終保持最新一段時(shí)間內(nèi)的數(shù)據(jù)統(tǒng)計(jì)。

基本概念

  • 窗口大?。夯瑒?dòng)窗口有一個(gè)固定的尺寸,表示你關(guān)心的數(shù)據(jù)的時(shí)間范圍或數(shù)據(jù)數(shù)量。例如,如果你關(guān)注的是過(guò)去5分鐘內(nèi)的數(shù)據(jù),那么窗口大小就是5分鐘。
  • 滑動(dòng)/移動(dòng):隨著時(shí)間的推移或新數(shù)據(jù)的到來(lái),窗口會(huì)不斷向前移動(dòng),丟棄最舊的數(shù)據(jù)點(diǎn),同時(shí)納入最新的數(shù)據(jù)點(diǎn),始終保持窗口內(nèi)數(shù)據(jù)的新鮮度。
  • 數(shù)據(jù)處理:在窗口內(nèi)的數(shù)據(jù)會(huì)被用來(lái)進(jìn)行各種計(jì)算,比如求平均值、最大值、最小值、計(jì)數(shù)等,具體取決于應(yīng)用場(chǎng)景。

應(yīng)用實(shí)例

  • 網(wǎng)絡(luò)流量控制:在網(wǎng)絡(luò)傳輸中,滑動(dòng)窗口常用來(lái)控制發(fā)送速率,避免擁塞。TCP協(xié)議中的擁塞控制就采用了類(lèi)似滑動(dòng)窗口的機(jī)制來(lái)調(diào)整數(shù)據(jù)包的發(fā)送速率。
  • 速率限制(Rate Limiting):在Web服務(wù)中,滑動(dòng)窗口算法可以用來(lái)實(shí)現(xiàn)對(duì)API調(diào)用或其他請(qǐng)求的速率限制,確保服務(wù)不會(huì)因?yàn)檫^(guò)多的請(qǐng)求而過(guò)載。通過(guò)控制窗口期內(nèi)的請(qǐng)求總數(shù)或特定時(shí)間段內(nèi)的請(qǐng)求頻率,可以平滑系統(tǒng)負(fù)載。
  • 交易監(jiān)控:在金融系統(tǒng)中,滑動(dòng)窗口可用于監(jiān)控交易活動(dòng),比如檢測(cè)是否存在異常交易模式,通過(guò)分析一段時(shí)間內(nèi)的交易頻次和金額分布。

實(shí)現(xiàn)要點(diǎn)

  • 數(shù)據(jù)結(jié)構(gòu)選擇:為了高效實(shí)現(xiàn)滑動(dòng)窗口,通常使用隊(duì)列或哈希表等數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)窗口內(nèi)的數(shù)據(jù),便于快速插入和刪除元素。
  • 窗口邊界處理:需要準(zhǔn)確地管理窗口的邊界,確保當(dāng)新數(shù)據(jù)到來(lái)時(shí),能及時(shí)移除窗口最左邊(或最舊)的數(shù)據(jù)點(diǎn),同時(shí)加入新數(shù)據(jù)點(diǎn)。
  • 時(shí)間復(fù)雜度:理想情況下,滑動(dòng)窗口算法的操作(如添加元素、移除元素、計(jì)算窗口內(nèi)統(tǒng)計(jì)量)應(yīng)該能在常數(shù)時(shí)間內(nèi)完成,以保證算法的高效性。

滑動(dòng)窗口算法因其靈活性和高效性,在眾多領(lǐng)域中都有重要應(yīng)用,是理解和處理時(shí)間序列數(shù)據(jù)的一個(gè)非常實(shí)用的工具。

要實(shí)現(xiàn)AOP結(jié)合滑動(dòng)窗口算法來(lái)實(shí)現(xiàn)自定義規(guī)則的限流,我們可以在原有的基礎(chǔ)上進(jìn)一步擴(kuò)展,以支持更靈活的配置和更復(fù)雜的規(guī)則。以下是一個(gè)基于Spring AOP和滑動(dòng)窗口算法的簡(jiǎn)單示例,包括自定義注解來(lái)設(shè)置限流規(guī)則,以及如何在切面中應(yīng)用這些規(guī)則。

定義緩存注解

首先,定義一個(gè)自定義注解來(lái)標(biāo)記需要限流的方法,并允許傳入限流的具體規(guī)則

package com.example.demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WindowRateLimit {
    // 允許的最大請(qǐng)求數(shù)
    int limit();
    // 窗口時(shí)間長(zhǎng)度,單位毫秒
    long timeWindowMilliseconds();
}

滑動(dòng)窗口限流器

接下來(lái),實(shí)現(xiàn)滑動(dòng)窗口限流器,這里簡(jiǎn)化處理,直接使用內(nèi)存實(shí)現(xiàn),實(shí)際應(yīng)用中可能需要基于Redis等持久化存儲(chǔ)以適應(yīng)分布式場(chǎng)景:

核心思想:每次請(qǐng)求進(jìn)來(lái)時(shí),獲取當(dāng)前時(shí)間的時(shí)間戳,將每次請(qǐng)求的時(shí)間戳存儲(chǔ)到LinkedList集合中,同時(shí)以當(dāng)前時(shí)間為窗口期的結(jié)束點(diǎn),刪除往前一個(gè)窗口期內(nèi)所有的請(qǐng)求時(shí)間戳,將LinkedList集合剩余數(shù)據(jù)的個(gè)數(shù)與自定義設(shè)置的窗口期請(qǐng)求峰值進(jìn)行對(duì)比,若等于則直接限流。

package com.example.demo.uitls;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.LinkedList;
/**
 * SlidingWindowRateLimiter : 滑動(dòng)窗口限流算法
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SlidingWindowRateLimiter implements Serializable {
    /**
     * 請(qǐng)求隊(duì)列
     */
    private LinkedList<Long> requests = new LinkedList<>();
    /**
     * 最大請(qǐng)求數(shù)
     */
    private int maxRequests;
    /**
     * 窗口大小
     */
    private long windowSizeInMilliseconds;
    public SlidingWindowRateLimiter(int maxRequests, long windowSizeInMilliseconds) {
        this.maxRequests = maxRequests;
        this.windowSizeInMilliseconds = windowSizeInMilliseconds;
    }
    /**
     * 判斷是否允許請(qǐng)求
     * @return
     */
    public synchronized boolean allowRequest() {
        // 獲取當(dāng)前時(shí)間
        long currentTime = System.currentTimeMillis();
        // 清除窗口之外的舊請(qǐng)求
        while (!requests.isEmpty() && currentTime - requests.peekFirst() > windowSizeInMilliseconds) {
            requests.removeFirst();
        }
        // 如果當(dāng)前窗口請(qǐng)求未達(dá)到上限,則允許請(qǐng)求并記錄
        if (requests.size() < maxRequests) {
            requests.addLast(currentTime);
            return true;
        } else {
            // 達(dá)到限流閾值,拒絕請(qǐng)求
            return false;
        }
    }
}

AOP切面實(shí)現(xiàn)

最后,創(chuàng)建AOP切面來(lái)應(yīng)用限流邏輯:

將需要限流的方法所初始化的滑動(dòng)窗口限流器緩存到Redis中,過(guò)期時(shí)間設(shè)置為對(duì)應(yīng)的窗口時(shí)間。

一個(gè)窗口時(shí)間內(nèi),若沒(méi)有新的請(qǐng)求進(jìn)來(lái),即存儲(chǔ)的請(qǐng)求時(shí)間戳都為窗口期外的,因此可以直接清除掉已減少緩存占用空間。

package com.example.demo.aspect;
import com.example.demo.annotation.WindowRateLimit;
import com.example.demo.config.redis.RedisKeyEnum;
import com.example.demo.uitls.RedisUtil;
import com.example.demo.uitls.SlidingWindowRateLimiter;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
 * RateLimiterAspect :
 */
@Aspect
@Component
public class SlidingWindowRateLimiterAspect {
    @Resource
    private RedisUtil redisUtil;
    @Around("@annotation(rateLimit)")
    public Object applyRateLimit(ProceedingJoinPoint joinPoint, WindowRateLimit rateLimit) throws Throwable {
        // 獲取調(diào)用的方法名
        String methodName = joinPoint.getSignature().getName();
        // 獲取方法對(duì)應(yīng)的緩存滑動(dòng)窗口限流器KEY
        String key = RedisKeyEnum.WINDOW_CURRENT_LIMITING.getKey() + methodName;
        // 從緩存中獲取滑動(dòng)窗口限流器
        SlidingWindowRateLimiter rateLimiter = redisUtil.getCacheObject(key);
        // 如果滑動(dòng)窗口限流器不存在,則創(chuàng)建一個(gè)新限流器
        if (rateLimiter == null) {
            rateLimiter = new SlidingWindowRateLimiter(rateLimit.limit(), rateLimit.timeWindowMilliseconds());
        }
        // 如果滑動(dòng)窗口限流器存在,則判斷是否允許請(qǐng)求
        if (!rateLimiter.allowRequest()) {
            throw new RuntimeException("Too many requests, please try again later.");
        }
        // 如果允許請(qǐng)求,則更新滑動(dòng)窗口限流器,緩存過(guò)期時(shí)間設(shè)置為滑動(dòng)窗口限流器時(shí)間窗口
        redisUtil.setCacheObject(key, rateLimiter, rateLimit.timeWindowMilliseconds(), TimeUnit.MILLISECONDS);
        // 允許執(zhí)行方法
        return joinPoint.proceed();
    }
}

應(yīng)用限流注解

在需要做限流的方法上加上注解,在注解參數(shù)中設(shè)定 允許的最大請(qǐng)求數(shù) 和 窗口時(shí)間長(zhǎng)度(單位毫秒)

package com.example.demo.service.impl;
import com.example.demo.annotation.WindowRateLimit;
import com.example.demo.service.TestService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class TestServiceImpl implements TestService {
    @Override
    @WindowRateLimit(limit = 5, timeWindowMilliseconds = 60L*1000) // 每最多允許5次請(qǐng)求
    public String getContent() {
        return "Hello Word";
    }
}

首次請(qǐng)求時(shí),初始化滑動(dòng)窗口限流器,記錄第一次請(qǐng)求的時(shí)間戳

窗口期內(nèi),記錄了五次請(qǐng)求的時(shí)間戳后,已達(dá)到我們?cè)谧⒔庵性O(shè)置的窗口期最大請(qǐng)求量

此時(shí)接口限流

到此這篇關(guān)于Java AOP實(shí)現(xiàn)自定義滑動(dòng)窗口限流器方法詳解的文章就介紹到這了,更多相關(guān)Java滑動(dòng)窗口限流器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java版AI五子棋游戲

    Java版AI五子棋游戲

    這篇文章主要為大家詳細(xì)介紹了Java版AI五子棋游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • Java中遍歷ConcurrentHashMap的四種方式詳解

    Java中遍歷ConcurrentHashMap的四種方式詳解

    這篇文章主要介紹了Java中遍歷ConcurrentHashMap的四種方式詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Spring?Boot整合ELK實(shí)現(xiàn)日志采集與監(jiān)控

    Spring?Boot整合ELK實(shí)現(xiàn)日志采集與監(jiān)控

    這篇文章主要介紹了Spring?Boot整合ELK實(shí)現(xiàn)日志采集與監(jiān)控,需要的朋友可以參考下
    2022-06-06
  • SpringBoot集成kafka全面實(shí)戰(zhàn)記錄

    SpringBoot集成kafka全面實(shí)戰(zhàn)記錄

    在實(shí)際開(kāi)發(fā)中,我們可能有這樣的需求,應(yīng)用A從TopicA獲取到消息,經(jīng)過(guò)處理后轉(zhuǎn)發(fā)到TopicB,再由應(yīng)用B監(jiān)聽(tīng)處理消息,即一個(gè)應(yīng)用處理完成后將該消息轉(zhuǎn)發(fā)至其他應(yīng)用,完成消息的轉(zhuǎn)發(fā),這篇文章主要介紹了SpringBoot集成kafka全面實(shí)戰(zhàn),需要的朋友可以參考下
    2021-11-11
  • 如何在Maven項(xiàng)目中運(yùn)行JUnit5測(cè)試用例實(shí)現(xiàn)

    如何在Maven項(xiàng)目中運(yùn)行JUnit5測(cè)試用例實(shí)現(xiàn)

    這篇文章主要介紹了如何在Maven項(xiàng)目中運(yùn)行JUnit5測(cè)試用例實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • java中字符串與日期的轉(zhuǎn)換實(shí)例

    java中字符串與日期的轉(zhuǎn)換實(shí)例

    java中字符串與日期的轉(zhuǎn)換實(shí)例,需要的朋友可以參考一下
    2013-05-05
  • springboot+redis 實(shí)現(xiàn)分布式限流令牌桶的示例代碼

    springboot+redis 實(shí)現(xiàn)分布式限流令牌桶的示例代碼

    這篇文章主要介紹了springboot+redis 實(shí)現(xiàn)分布式限流令牌桶 ,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • springboot+redis+lua實(shí)現(xiàn)分布式鎖的腳本

    springboot+redis+lua實(shí)現(xiàn)分布式鎖的腳本

    本文介紹了如何使用Spring Boot、Redis和Lua腳本實(shí)現(xiàn)分布式鎖,包括實(shí)現(xiàn)原理、代碼實(shí)現(xiàn)和存在的問(wèn)題,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • 解決RedisTemplate調(diào)用increment報(bào)錯(cuò)問(wèn)題

    解決RedisTemplate調(diào)用increment報(bào)錯(cuò)問(wèn)題

    這篇文章主要介紹了解決RedisTemplate調(diào)用increment報(bào)錯(cuò)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • java網(wǎng)絡(luò)編程之識(shí)別示例 獲取主機(jī)網(wǎng)絡(luò)接口列表

    java網(wǎng)絡(luò)編程之識(shí)別示例 獲取主機(jī)網(wǎng)絡(luò)接口列表

    一個(gè)客戶(hù)端想要發(fā)起一次通信,先決條件就是需要知道運(yùn)行著服務(wù)器端程序的主機(jī)的IP地址是多少。然后我們才能夠通過(guò)這個(gè)地址向服務(wù)器發(fā)送信息。
    2014-01-01

最新評(píng)論