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

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

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

1.基礎(chǔ)介紹

1.1. 限流場景

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

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

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

在每個(gè)請求到達(dá)時(shí),檢查計(jì)數(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ù)實(shí)際情況修改以下屬性:

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

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

  • spring.redis.jedis.pool.max-active:連接池中的最大活動(dòng)連接數(shù)。
  • spring.redis.jedis.pool.max-idle:連接池中的最大空閑連接數(shù)。
  • spring.redis.jedis.pool.min-idle:連接池中的最小空閑連接數(shù)。
  • spring.redis.jedis.pool.max-wait:從連接池獲取連接的最大等待時(shí)間(毫秒),-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ù)您的實(shí)際Redis服務(wù)器配置進(jìn)行調(diào)整,并根據(jù)需要添加其他相關(guān)配置,如超時(shí)設(shè)置、SSL配置等。

2.3. 核心源碼

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

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

    示例中,RequestLimitFilter是一個(gè)實(shí)現(xiàn)了javax.servlet.Filter接口的請求限流過濾器。它使用Redis的計(jì)數(shù)器功能來實(shí)現(xiàn)請求限流邏輯。每個(gè)請求到達(dá)時(shí),根據(jù)客戶端的IP地址作為Redis的鍵,增加計(jì)數(shù)器的值并設(shè)置過期時(shí)間為指定的時(shí)間窗口。如果計(jì)數(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; // 時(shí)間窗口(單位:秒)
    @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; // 時(shí)間窗口(單位:秒)
    @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)化一下加入布隆過濾器

使用布隆過濾器減少對(duì)Redis的訪問:布隆過濾器是一種高效的概率數(shù)據(jù)結(jié)構(gòu),可以用于快速判斷元素是否存在于集合中。在限制請求頻率時(shí),可以使用布隆過濾器來減少對(duì)Redis的訪問。只有在布隆過濾器判斷請求不是重復(fù)請求時(shí),才進(jìn)行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; // 時(shí)間窗口(單位:秒)
    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地址的邏輯
        // ...
    }
}

在上述代碼中,我們引入了布隆過濾器來減少對(duì)Redis的訪問。如果布隆過濾器判斷請求可能是重復(fù)請求,則直接放行,無需進(jìn)行Redis操作。同時(shí),我們還添加了對(duì)Redis操作異常的處理,并在限流超過閾值時(shí)刪除新創(chuàng)建的鍵,以避免無限增長。請根據(jù)實(shí)際情況進(jìn)行適當(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á)時(shí)執(zhí)行限流邏輯。

3.總結(jié)

其實(shí)上面我們寫完的還是有問題的

  • 如果系統(tǒng)部署在多個(gè)節(jié)點(diǎn)上,可以考慮使用分布式限流算法,如令牌桶算法或漏桶算法。這些算法可以在分布式環(huán)境中平衡請求的處理,并保證全局的請求限制。
  • 將請求限流的參數(shù),如請求限制數(shù)量和時(shí)間窗口,配置為可動(dòng)態(tài)調(diào)整的參數(shù)。可以使用注解或配置文件來管理這些參數(shù),以便在運(yùn)行時(shí)進(jìn)行調(diào)整,而無需重新編譯代碼。

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

相關(guān)文章

  • 快速解決跨域請求問題:jsonp和CORS

    快速解決跨域請求問題:jsonp和CORS

    這篇文章主要介紹了快速解決跨域請求問題:jsonp和CORS,涉及jsonp和CORS的介紹,分享了前端 jQuery 寫法,后端 SpringMVC 配置,后端非 SpringMVC 配置等相關(guān)內(nèi)容,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-11-11
  • idea中解決maven包沖突的問題(maven helper)

    idea中解決maven包沖突的問題(maven helper)

    這篇文章主要介紹了idea中解決maven包沖突的問題(maven helper),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-12-12
  • Java中BigDecimal精度和相等比較的坑

    Java中BigDecimal精度和相等比較的坑

    BigDecimal是一種精確的數(shù)字類,一般用于高精度的開發(fā)領(lǐng)域中,例如銀行。下面這篇文章主要給大家介紹了關(guān)于Java中BigDecimal精度和相等比較的坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-09-09
  • JWT登錄認(rèn)證Springboot詳解

    JWT登錄認(rèn)證Springboot詳解

    文章主要介紹了如何在Java項(xiàng)目中使用JWT進(jìn)行用戶認(rèn)證和授權(quán),通過定義一個(gè)常量,編寫JWT工具類來生成和解析token,登錄時(shí)在服務(wù)端生成token并返回給客戶端,客戶端使用攔截器攔截請求,驗(yàn)證token的有效性,從而實(shí)現(xiàn)權(quán)限控制,文章旨在分享個(gè)人經(jīng)驗(yàn),為開發(fā)者提供參考
    2024-11-11
  • SpringBoot?AOP統(tǒng)一處理Web請求日志的示例代碼

    SpringBoot?AOP統(tǒng)一處理Web請求日志的示例代碼

    springboot有很多方法處理日志,例如攔截器,aop切面,service中代碼記錄等,下面這篇文章主要給大家介紹了關(guān)于SpringBoot?AOP統(tǒng)一處理Web請求日志的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • Java實(shí)現(xiàn)UDP多線程在線咨詢

    Java實(shí)現(xiàn)UDP多線程在線咨詢

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)UDP多線程在線咨詢,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • java LRU(Least Recently Used )詳解及實(shí)例代碼

    java LRU(Least Recently Used )詳解及實(shí)例代碼

    這篇文章主要介紹了java LRU(Least Recently Used )詳解及實(shí)例代碼的相關(guān)資料,Java里面實(shí)現(xiàn)LRU緩存通常有兩種選擇,一種是使用LinkedHashMap,一種是自己設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu),使用鏈表+HashMap,需要的朋友可以參考下
    2016-11-11
  • 解決Mybatis中mapper.xml文件update,delete及insert返回值問題

    解決Mybatis中mapper.xml文件update,delete及insert返回值問題

    這篇文章主要介紹了解決Mybatis中mapper.xml文件update,delete及insert返回值問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • Java gbk轉(zhuǎn)utf-8

    Java gbk轉(zhuǎn)utf-8

    本文主要介紹了Java gbk轉(zhuǎn)utf-8的方法,具有一定的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02
  • java8中定時(shí)任務(wù)最佳實(shí)現(xiàn)方式(實(shí)現(xiàn)原理)

    java8中定時(shí)任務(wù)最佳實(shí)現(xiàn)方式(實(shí)現(xiàn)原理)

    這篇文章主要介紹了java8中定時(shí)任務(wù)最佳實(shí)現(xiàn)方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-12-12

最新評(píng)論