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

基于SpringBoot+Redis實現(xiàn)一個簡單的限流器

 更新時間:2023年08月15日 11:08:57   作者:冰點.  
在Spring?Boot中使用Redis和過濾器實現(xiàn)請求限流,過濾器將在每個請求到達(dá)時檢查請求頻率,并根據(jù)設(shè)定的閾值進行限制,這樣可以保護您的應(yīng)用程序免受惡意請求或高并發(fā)請求的影響,本文我們通過Spring?Boot?+Redis?實現(xiàn)一個輕量級的消息隊列,需要的朋友可以參考下

1.基礎(chǔ)介紹

1.1. 限流場景

假設(shè)我們有一個API接口,需要限制每個用戶在一段時間內(nèi)的請求頻率。比如每秒只允許請求100次等等的業(yè)務(wù)需求。

1.2. 實現(xiàn)限流邏輯:

使用Redis的計數(shù)器功能可以實現(xiàn)基于時間窗口的限流算法。通過在Redis中存儲請求計數(shù)器和過期時間,可以控制單位時間內(nèi)的請求頻率。在需要進行限流的接口或方法中,使用Redis的原子操作(如INCR和EXPIRE)來增加計數(shù)器并設(shè)置過期時間。

在每個請求到達(dá)時,檢查計數(shù)器的值是否超過設(shè)定的閾值,如果超過則拒絕請求,否則允許請求繼續(xù)執(zhí)行。

2.步驟

2.1. 引入依賴

<dependencies>
    <!-- Spring Data Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>

2.2. 配置文件

# Redis連接配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=your_password
spring.redis.database=0
# Redis連接池配置
spring.redis.jedis.pool.max-active=50
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=5
spring.redis.jedis.pool.max-wait=-1

在上面的配置中,您可以根據(jù)實際情況修改以下屬性:

  • spring.redis.host:Redis服務(wù)器的主機名或IP地址。
  • spring.redis.port:Redis服務(wù)器的端口號。
  • spring.redis.password:Redis服務(wù)器的密碼(如果有的話)。
  • spring.redis.database:Redis數(shù)據(jù)庫的索引,默認(rèn)為0。

另外,您還可以配置Redis連接池的屬性,以控制連接池的行為。在示例配置中,設(shè)置了以下連接池屬性:

  • spring.redis.jedis.pool.max-active:連接池中的最大活動連接數(shù)。
  • spring.redis.jedis.pool.max-idle:連接池中的最大空閑連接數(shù)。
  • spring.redis.jedis.pool.min-idle:連接池中的最小空閑連接數(shù)。
  • spring.redis.jedis.pool.max-wait:從連接池獲取連接的最大等待時間(毫秒),-1表示無限等待。

如果 使用的是YAML格式的配置文件(application.yml),可以將上述配置轉(zhuǎn)換為相應(yīng)的格式:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: your_password
    database: 0
  redis.jedis.pool:
    max-active: 50
    max-idle: 10
    min-idle: 5
    max-wait: -1

請根據(jù)您的實際Redis服務(wù)器配置進行調(diào)整,并根據(jù)需要添加其他相關(guān)配置,如超時設(shè)置、SSL配置等。

2.3. 核心源碼

  • 實現(xiàn)請求限流過濾器

    創(chuàng)建一個實現(xiàn)javax.servlet.Filter接口的請求限流過濾器。在過濾器中,使用Redis的計數(shù)器功能來實現(xiàn)請求限流邏輯。

    示例中,RequestLimitFilter是一個實現(xiàn)了javax.servlet.Filter接口的請求限流過濾器。它使用Redis的計數(shù)器功能來實現(xiàn)請求限流邏輯。每個請求到達(dá)時,根據(jù)客戶端的IP地址作為Redis的鍵,增加計數(shù)器的值并設(shè)置過期時間為指定的時間窗口。如果計數(shù)器超過了設(shè)定的閾值(這里是100),則返回HTTP 429 Too Many Requests響應(yīng)

示例中使用的是RedisTemplate<String, String>來操作Redis, 可以根據(jù)需要調(diào)整為適合您的數(shù)據(jù)類型和操作方式的RedisTemplate。

@Component
public class RequestLimitFilter implements Filter {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    private static final String REQUEST_LIMIT_PREFIX = "requestLimit:";
    private static final long REQUEST_LIMIT = 100; // 請求限制數(shù)量
    private static final long TIME_WINDOW = 60; // 時間窗口(單位:秒)
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String ipAddress = getClientIpAddress(httpRequest);
        String key = REQUEST_LIMIT_PREFIX + ipAddress;
        Long counter = redisTemplate.opsForValue().increment(key, 1);
        if (counter == 1) {
            redisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS);
        }
        if (counter > REQUEST_LIMIT) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            httpResponse.getWriter().write("請求頻率超過限制,請稍后再試!");
            return;
        }
        chain.doFilter(request, response);
    }
  private String getClientIpAddress(HttpServletRequest request) {
    String ipAddress = request.getHeader("X-Forwarded-For");
    if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
        ipAddress = request.getHeader("Proxy-Client-IP");
    }
    if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
        ipAddress = request.getHeader("WL-Proxy-Client-IP");
    }
    if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
        ipAddress = request.getRemoteAddr();
    }
    return ipAddress;
}
}

優(yōu)化后

public class RequestLimitFilter implements Filter {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    private static final String REQUEST_LIMIT_PREFIX = "requestLimit:";
    private static final long REQUEST_LIMIT = 100; // 請求限制數(shù)量
    private static final long TIME_WINDOW = 60; // 時間窗口(單位:秒)
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String ipAddress = getClientIpAddress(httpRequest);
        String key = REQUEST_LIMIT_PREFIX + ipAddress;
        Long counter = redisTemplate.opsForValue().increment(key, 1);
        if (counter == 1) {
            redisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS);
        }
        if (counter > REQUEST_LIMIT) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            try (PrintWriter writer = httpResponse.getWriter()) {
                writer.write("請求頻率超過限制,請稍后再試!");
            }
            return;
        }
        chain.doFilter(request, response);
    }
    private String getClientIpAddress(HttpServletRequest request) {
      ...
        return ipAddress;
    }
}

再優(yōu)化一下加入布隆過濾器

使用布隆過濾器減少對Redis的訪問:布隆過濾器是一種高效的概率數(shù)據(jù)結(jié)構(gòu),可以用于快速判斷元素是否存在于集合中。在限制請求頻率時,可以使用布隆過濾器來減少對Redis的訪問。只有在布隆過濾器判斷請求不是重復(fù)請求時,才進行Redis操作。

public class RequestLimitFilter implements Filter {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    private static final String REQUEST_LIMIT_PREFIX = "requestLimit:";
    private static final long REQUEST_LIMIT = 100; // 請求限制數(shù)量
    private static final long TIME_WINDOW = 60; // 時間窗口(單位:秒)
    private BloomFilter<String> bloomFilter;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化布隆過濾器
        int expectedInsertions = 1000; // 預(yù)期插入數(shù)量
        double falsePositiveProbability = 0.01; // 誤判率
        bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), expectedInsertions, falsePositiveProbability);
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String ipAddress = getClientIpAddress(httpRequest);
        if (bloomFilter.mightContain(ipAddress)) {
            // 布隆過濾器判斷可能是重復(fù)請求,直接放行
            chain.doFilter(request, response);
            return;
        }
        String key = REQUEST_LIMIT_PREFIX + ipAddress;
        Long counter;
        boolean isNewKey = false;
        try {
            counter = redisTemplate.opsForValue().increment(key, 1);
            if (counter == 1) {
                redisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS);
                isNewKey = true;
            }
        } catch (Exception e) {
            // 處理Redis操作異常
            // 可以選擇記錄日志或采取適當(dāng)?shù)奶幚泶胧?
            e.printStackTrace();
            chain.doFilter(request, response);
            return;
        }
        if (counter > REQUEST_LIMIT) {
            if (isNewKey) {
                // 刪除新創(chuàng)建的鍵,避免無限增長
                redisTemplate.delete(key);
            }
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            try (PrintWriter writer = httpResponse.getWriter()) {
                writer.write("請求頻率超過限制,請稍后再試!");
            }
            return;
        }
        bloomFilter.put(ipAddress); // 將IP地址添加到布隆過濾器
        chain.doFilter(request, response);
    }
    @Override
    public void destroy() {
        // 清理資源,如關(guān)閉Redis連接等
    }
    private String getClientIpAddress(HttpServletRequest request) {
        // 獲取客戶端IP地址的邏輯
        // ...
    }
}

在上述代碼中,我們引入了布隆過濾器來減少對Redis的訪問。如果布隆過濾器判斷請求可能是重復(fù)請求,則直接放行,無需進行Redis操作。同時,我們還添加了對Redis操作異常的處理,并在限流超過閾值時刪除新創(chuàng)建的鍵,以避免無限增長。請根據(jù)實際情況進行適當(dāng)調(diào)整和完善。

  • 注冊過濾器
    在Spring Boot應(yīng)用程序的配置類中注冊過濾器,以便它能夠在請求處理過程中生效。
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private RequestLimitFilter requestLimitFilter;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(requestLimitFilter);
    }
}

通過將過濾器添加到addInterceptors方法中,它將被注冊為Spring Boot應(yīng)用程序的全局過濾器,并在請求到達(dá)時執(zhí)行限流邏輯。

3.總結(jié)

其實上面我們寫完的還是有問題的

  • 如果系統(tǒng)部署在多個節(jié)點上,可以考慮使用分布式限流算法,如令牌桶算法或漏桶算法。這些算法可以在分布式環(huán)境中平衡請求的處理,并保證全局的請求限制。
  • 將請求限流的參數(shù),如請求限制數(shù)量和時間窗口,配置為可動態(tài)調(diào)整的參數(shù)??梢允褂米⒔饣蚺渲梦募砉芾磉@些參數(shù),以便在運行時進行調(diào)整,而無需重新編譯代碼。

到此這篇關(guān)于基于SpringBoot+Redis實現(xiàn)一個簡單的限流器的文章就介紹到這了,更多相關(guān)SpringBoot+Redis實現(xiàn)限流器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring在代碼中獲取bean的幾種方式詳解

    Spring在代碼中獲取bean的幾種方式詳解

    這篇文章主要介紹了Spring在代碼中獲取bean的幾種方式詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-08-08
  • Java單例模式實例簡述

    Java單例模式實例簡述

    這篇文章主要介紹了Java單例模式,在Java應(yīng)用程序設(shè)計中有著非常重要的作用,本文以實例形式對此加以簡單分析,需要的朋友可以參考下
    2014-09-09
  • 關(guān)于Spring3 + Mybatis3整合時多數(shù)據(jù)源動態(tài)切換的問題

    關(guān)于Spring3 + Mybatis3整合時多數(shù)據(jù)源動態(tài)切換的問題

    這篇文章主要介紹了關(guān)于Spring3 + Mybatis3整合時多數(shù)據(jù)源動態(tài)切換的問題,需要的朋友可以參考下
    2017-04-04
  • 使用Flyway進行Java數(shù)據(jù)庫版本控制的操作指南

    使用Flyway進行Java數(shù)據(jù)庫版本控制的操作指南

    今天我們將深入探討如何使用Flyway進行Java數(shù)據(jù)庫版本控制,Flyway是一個流行的數(shù)據(jù)庫遷移工具,用于管理和自動化數(shù)據(jù)庫模式的演變,文中通過代碼示例介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-07-07
  • 為何修改equals方法時還要重寫hashcode方法的原因分析

    為何修改equals方法時還要重寫hashcode方法的原因分析

    這篇文章主要介紹了為何修改equals方法時還要重寫hashcode方法的原因分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • java項目中常用指標(biāo)UV?PV?QPS?TPS含義以及統(tǒng)計方法

    java項目中常用指標(biāo)UV?PV?QPS?TPS含義以及統(tǒng)計方法

    文章介紹了現(xiàn)代Web應(yīng)用中性能監(jiān)控和分析的重要性,涵蓋了UV、PV、QPS、TPS等關(guān)鍵指標(biāo)的統(tǒng)計方法,并提供了示例代碼,同時,文章還討論了性能優(yōu)化和瓶頸分析的策略,以及使用Grafana等可視化工具進行監(jiān)控與告警的重要性
    2025-01-01
  • 一篇文章帶你入門Java多線程

    一篇文章帶你入門Java多線程

    這篇文章主要介紹了java多線程編程實例,分享了幾則多線程的實例代碼,具有一定參考價值,加深多線程編程的理解還是很有幫助的,需要的朋友可以參考下
    2021-08-08
  • Spring基礎(chǔ)之AOP的概念介紹

    Spring基礎(chǔ)之AOP的概念介紹

    AOP是Spring的關(guān)鍵特性之一,雖然Spring的IOC特性并不依賴于AOP,本文重點介紹AOP編程中的一些術(shù)語,這些術(shù)語不僅僅局限于Spring,它適用于所有的AOP編程,感興趣的朋友一起看看吧
    2022-06-06
  • SpringBoot使用hutool-captcha實現(xiàn)驗證碼生成與驗證

    SpringBoot使用hutool-captcha實現(xiàn)驗證碼生成與驗證

    在springboot的登陸頁面中為了防止機器大規(guī)模注冊,機器暴力破解數(shù)據(jù)密碼等危害,需要驗證隨機生成的驗證碼,本文主要介紹了SpringBoot使用hutool-captcha實現(xiàn)驗證碼生成與驗證,感興趣的可以了解一下
    2023-12-12
  • 一文淺析SpringBoot3中的異步批量處理操作

    一文淺析SpringBoot3中的異步批量處理操作

    對于使用 Spring Boot 3 框架的開發(fā)者而言,掌握異步批量處理操作至關(guān)重要,本文將深入探討 Spring Boot 3 中異步批量處理操作的方方面面,幫助各位開發(fā)者在實際項目中更好地應(yīng)用這一強大功能
    2025-12-12

最新評論

临沧市| 嘉义县| 天峻县| 崇阳县| 尼勒克县| 庄浪县| 玉田县| 辽宁省| 射洪县| 富民县| 稻城县| 合山市| 扬州市| 荃湾区| 全南县| 乌审旗| 浠水县| 梅河口市| 巴南区| 阳春市| 锡林浩特市| 藁城市| 浏阳市| 宁蒗| 彰化市| 盐边县| 牟定县| 丹阳市| 潜山县| 晋江市| 雅江县| 商洛市| 浙江省| 文水县| 怀来县| 台湾省| 同仁县| 南开区| 北川| 双城市| 裕民县|