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

Spring Boot使用Redisson實(shí)現(xiàn)滑動(dòng)窗口限流的項(xiàng)目實(shí)踐

 更新時(shí)間:2024年03月03日 10:59:24   作者:冥胖胖9  
滑動(dòng)窗口限流是一種流量控制策略,用于控制在一定時(shí)間內(nèi)的請(qǐng)求頻率,本文主要介紹了Spring Boot使用Redisson實(shí)現(xiàn)滑動(dòng)窗口限流的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下

一、背景

在一些業(yè)務(wù)場(chǎng)景中,我們可能會(huì)提供一些API接口來(lái)給用戶使用,這些接口是不需要認(rèn)證的。比如:忘記密碼,重置密碼的接口,這些接口用戶可以隨意調(diào)用。為了防止這些接口被攻擊者惡意頻繁調(diào)用,消耗我們的系統(tǒng)資源,通常我們會(huì)對(duì)這些接口做限流保護(hù),否則可能會(huì)導(dǎo)致我們的服務(wù)器的宕機(jī)。

其實(shí)限流可以認(rèn)為是服務(wù)降級(jí)的一種,限流通過限制請(qǐng)求的流量以達(dá)到保護(hù)系統(tǒng)的目的。今天我們一起來(lái)討論一下在Redis緩存中怎么通過Redisson實(shí)現(xiàn)滑動(dòng)窗口限流。

二、滑動(dòng)窗口限流算法

1. 原理

我們先來(lái)了解一下滑動(dòng)窗口限流的實(shí)現(xiàn)原理,話不多說,先上圖。

從上圖我們可以得知,有一個(gè)時(shí)間窗口隨著時(shí)間軸在向右移動(dòng),我們可以記錄這個(gè)時(shí)間窗口內(nèi)的請(qǐng)求次數(shù),當(dāng)超過我們?cè)试S的閾值時(shí),則進(jìn)行限流不能繼續(xù)進(jìn)行請(qǐng)求。

2. 具體實(shí)現(xiàn)步驟

主要實(shí)現(xiàn)步驟如下:

  • 我們需要定義一個(gè)時(shí)間窗口,例如窗口大小為1分鐘。
  • 每次有請(qǐng)求時(shí),使用redis中的zset將這條請(qǐng)求記錄下來(lái)。                                                     充分利用zset這個(gè)數(shù)據(jù)結(jié)果的特點(diǎn),zet一條記錄由兩個(gè)成員(score和member)來(lái)表示,并且會(huì)按照score進(jìn)行排序。方便我們后面進(jìn)行刪除滑動(dòng)后窗口之前的請(qǐng)求記錄,和統(tǒng)計(jì)當(dāng)前窗口內(nèi)請(qǐng)求總數(shù)。
  • 通過ZREMRANGEBYSCORE命令,刪除zset中當(dāng)前窗口之前的請(qǐng)求記錄。
  • 通過ZCARD命令,統(tǒng)計(jì)當(dāng)前窗口內(nèi)的請(qǐng)求數(shù)量。

所以,我們使用滑動(dòng)窗口的思想是,只保留當(dāng)前時(shí)間窗口類的請(qǐng)求記錄,而丟棄當(dāng)前窗口之外的記錄。當(dāng)下次有請(qǐng)求進(jìn)來(lái)時(shí),我們只需要判斷當(dāng)前窗口內(nèi)的請(qǐng)求是否超過閾值就可以了。未超過則放行,超過則限流。

3. 偽代碼

根據(jù)以上步驟我們寫了一段偽代碼,如果考慮并發(fā)場(chǎng)景則需要考慮使用Lua腳本。

   public boolean allowRequest(String requestKey) {
        // 定義一個(gè)時(shí)間窗口為1分鐘
        long windowSize = Duration.ofMinutes(1).toSeconds();

        // 定義時(shí)間窗口內(nèi)請(qǐng)求閾值為100
        long limit = 100;

        // 當(dāng)前時(shí)間戳
        long currentTime = System.currentTimeMillis();

        // 窗口開始時(shí)間為當(dāng)前時(shí)間戳 - 窗口大小
        long windowStart = currentTime - windowSize * 1000;

        // 刪除當(dāng)前窗口開始之前的所有數(shù)據(jù)
        Jedis.zremrangeByScore(requestKey, "0", String.valueOf(windowStart));

        // 計(jì)算當(dāng)前窗口內(nèi)的請(qǐng)求總數(shù)
        long count = Jedis.zcard(requestKey);

        if (count < limit) {
            // 如果允許訪問,則將當(dāng)前請(qǐng)求加到窗口內(nèi)
            Jedis.zadd(requestKey, currentTime, String.valueOf(currentTime));
            return true;
        }
        return false;
    }
}

三、怎么使用Redisson進(jìn)行滑動(dòng)窗口限流

在redisson中已經(jīng)為我們實(shí)現(xiàn)好了滑動(dòng)窗口限流,通過redissonClient拿到限流器后,配置好時(shí)間窗口和限流速率就能直接使用了。實(shí)現(xiàn)原理和上面我們的偽代碼是一樣的,只是它將這一部分封裝好了,我們拿到后開箱即用。直接上代碼:

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;

public class RateLimiterFilter extends OncePerRequestFilter {

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

    private static final String RATE_LIMIT_KEY = "rateLimit:yourApiKey";
    private static final int MAX_REQUESTS_PER_MINUTE = 10;

    private final RedissonClient redissonClient;

    public RateLimiterFilter(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(RATE_LIMIT_KEY);
        // rateLimiter.trySetRate就是設(shè)置限流參數(shù),RateType有兩種,OVERALL是全局限流 ,PER_CLIENT是單Client限流(可以認(rèn)為就是單機(jī)限流),這里我們只討論全局模式。
        // 而后面三個(gè)參數(shù)的作用就是設(shè)置在多長(zhǎng)時(shí)間窗口內(nèi)(rateInterval+IntervalUnit),許可總量不超過多少(rate)
        // 上面代碼中我設(shè)置的值就是1分鐘內(nèi)總許可數(shù)不超過10個(gè)
        rateLimiter.trySetRate(RateType.OVERALL, MAX_REQUESTS_PER_MINUTE, 1, RateIntervalUnit.MINUTES);
        // 調(diào)用rateLimiter的tryAcquire()或者acquire()方法即可獲取許可
        if (!rateLimiter.tryAcquire()) {
            
            String path = request.getRequestURI().substring(request.getContextPath().length());
            log.error("當(dāng)前請(qǐng)求觸發(fā)限流策略,請(qǐng)求: {} 已經(jīng)被限流.", path);

            response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            response.getWriter().write("Too many requests!");
            return;
        }
        filterChain.doFilter(request, response);
    }
}

四、滑動(dòng)窗口限流的優(yōu)勢(shì)

滑動(dòng)窗口限流是一種流量控制策略,用于控制在一定時(shí)間內(nèi)的請(qǐng)求頻率。它的主要優(yōu)點(diǎn)是可以在單位時(shí)間內(nèi)平滑的控制流量,而不是簡(jiǎn)單的設(shè)置固定的請(qǐng)求數(shù)或速率。這使得系統(tǒng)可以更靈活的應(yīng)對(duì)突發(fā)流量或峰值流量,而不會(huì)因?yàn)楣潭ㄋ俾实南拗贫速M(fèi)或降低系統(tǒng)性能。

這種限流算法可以在分布式系統(tǒng)、API服務(wù)等各種場(chǎng)景中使用,以確保系統(tǒng)的穩(wěn)定性和可用性,防止過多的請(qǐng)求或惡意請(qǐng)求對(duì)系統(tǒng)造成負(fù)擔(dān)或崩潰。

到此這篇關(guān)于Spring Boot使用Redisson實(shí)現(xiàn)滑動(dòng)窗口限流的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)SpringBoot Redisson滑動(dòng)窗口限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論