SpringBoot接口訪(fǎng)問(wèn)頻率限制的實(shí)現(xiàn)方式
概述
接口訪(fǎng)問(wèn)頻率限制是通過(guò)在一定時(shí)間內(nèi)限制用戶(hù)對(duì)接口的訪(fǎng)問(wèn)次數(shù)來(lái)實(shí)現(xiàn)的。常見(jiàn)的限流算法包括令牌桶算法(Token Bucket)、漏桶算法(Leaky Bucket)、固定窗口計(jì)數(shù)器(Fixed Window Counter)和滑動(dòng)窗口計(jì)數(shù)器(Sliding Window Counter)等。在Spring Boot中,我們可以通過(guò)多種方式來(lái)實(shí)現(xiàn)接口的限流,如使用過(guò)濾器、攔截器或者借助第三方庫(kù)。
為什么需要接口訪(fǎng)問(wèn)頻率限制
- 防止惡意攻擊:通過(guò)限制接口的訪(fǎng)問(wèn)頻率,可以有效防止惡意用戶(hù)或機(jī)器人頻繁訪(fǎng)問(wèn)接口,導(dǎo)致系統(tǒng)資源耗盡。
- 提升系統(tǒng)穩(wěn)定性:在高并發(fā)場(chǎng)景下,限流可以有效保護(hù)后端服務(wù),避免因流量過(guò)大而導(dǎo)致系統(tǒng)崩潰。
- 提升用戶(hù)體驗(yàn):合理的限流可以保障所有用戶(hù)都能獲得較好的服務(wù)質(zhì)量,避免個(gè)別用戶(hù)過(guò)度使用資源。
常見(jiàn)的實(shí)現(xiàn)方式
基于過(guò)濾器的實(shí)現(xiàn)
過(guò)濾器是Java Web應(yīng)用中常用的一種組件,它可以在請(qǐng)求到達(dá)Servlet之前對(duì)請(qǐng)求進(jìn)行預(yù)處理。通過(guò)在過(guò)濾器中實(shí)現(xiàn)限流邏輯,可以對(duì)所有的HTTP請(qǐng)求進(jìn)行統(tǒng)一的限流控制。
基于攔截器的實(shí)現(xiàn)
攔截器是Spring框架提供的一種處理器,可以在請(qǐng)求處理之前和之后進(jìn)行相關(guān)操作。相比于過(guò)濾器,攔截器可以更加細(xì)粒度地控制請(qǐng)求,適用于需要針對(duì)某些特定接口進(jìn)行限流的場(chǎng)景。
基于第三方庫(kù)Bucket4j的實(shí)現(xiàn)
Bucket4j是一個(gè)Java實(shí)現(xiàn)的高性能限流庫(kù),它支持多種限流算法,如令牌桶算法。通過(guò)使用Bucket4j,可以輕松地在Spring Boot應(yīng)用中實(shí)現(xiàn)復(fù)雜的限流邏輯,并且它還提供了豐富的配置選項(xiàng)和統(tǒng)計(jì)功能。
實(shí)際代碼示例
基于過(guò)濾器實(shí)現(xiàn)Rate Limiting
首先,我們需要?jiǎng)?chuàng)建一個(gè)自定義的過(guò)濾器類(lèi),并在其中實(shí)現(xiàn)限流邏輯。以下是一個(gè)示例代碼:
import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; public class RateLimitingFilter implements Filter { private final ConcurrentMap<String, Long> requestCounts = new ConcurrentHashMap<>(); private static final long ALLOWED_REQUESTS_PER_MINUTE = 60; @Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化過(guò)濾器 } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String clientIp = httpRequest.getRemoteAddr(); long currentTimeMillis = System.currentTimeMillis(); requestCounts.putIfAbsent(clientIp, currentTimeMillis); long lastRequestTime = requestCounts.get(clientIp); if (TimeUnit.MILLISECONDS.toMinutes(currentTimeMillis - lastRequestTime) < 1) { long requestCount = requestCounts.values().stream().filter(time -> TimeUnit.MILLISECONDS.toMinutes(currentTimeMillis - time) < 1).count(); if (requestCount > ALLOWED_REQUESTS_PER_MINUTE) { httpResponse.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS); httpResponse.getWriter().write("Too many requests"); return; } } requestCounts.put(clientIp, currentTimeMillis); chain.doFilter(request, response); } @Override public void destroy() { // 銷(xiāo)毀過(guò)濾器 } }
然后,在Spring Boot應(yīng)用的配置類(lèi)中注冊(cè)這個(gè)過(guò)濾器:
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<RateLimitingFilter> loggingFilter(){ FilterRegistrationBean<RateLimitingFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new RateLimitingFilter()); registrationBean.addUrlPatterns("/api/*"); return registrationBean; } }
基于攔截器實(shí)現(xiàn)Rate Limiting
首先,我們需要?jiǎng)?chuàng)建一個(gè)自定義的攔截器類(lèi),并在其中實(shí)現(xiàn)限流邏輯。以下是一個(gè)示例代碼:
import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; public class RateLimitingInterceptor implements HandlerInterceptor { private final ConcurrentMap<String, Long> requestCounts = new ConcurrentHashMap<>(); private static final long ALLOWED_REQUESTS_PER_MINUTE = 60; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String clientIp = request.getRemoteAddr(); long currentTimeMillis = System.currentTimeMillis(); requestCounts.putIfAbsent(clientIp, currentTimeMillis); long lastRequestTime = requestCounts.get(clientIp); if (TimeUnit.MILLISECONDS.toMinutes(currentTimeMillis - lastRequestTime) < 1) { long requestCount = requestCounts.values().stream().filter(time -> TimeUnit.MILLISECONDS.toMinutes(currentTimeMillis - time) < 1).count(); if (requestCount > ALLOWED_REQUESTS_PER_MINUTE) { response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS); response.getWriter().write("Too many requests"); return false; } } requestCounts.put(clientIp, currentTimeMillis); return true; } }
然后,在Spring Boot應(yīng)用的配置類(lèi)中注冊(cè)這個(gè)攔截器:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private RateLimitingInterceptor rateLimitingInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(rateLimitingInterceptor).addPathPatterns("/api/**"); } }
使用Bucket4j實(shí)現(xiàn)Rate Limiting
首先,在項(xiàng)目中引入Bucket4j依賴(lài):
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-core</artifactId> <version>7.0.0</version> </dependency>
然后,創(chuàng)建一個(gè)自定義的過(guò)濾器類(lèi),并在其中實(shí)現(xiàn)限流邏輯:
import io.github.bucket4j.Bandwidth; import io.github.bucket4j.Bucket; import io.github.bucket4j.Bucket4j; import io.github.bucket4j.Refill; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.time.Duration; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class Bucket4jRateLimitingFilter implements Filter { private final ConcurrentMap<String, Bucket> buckets = new ConcurrentHashMap<>(); @ Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化過(guò)濾器 } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String clientIp = httpRequest.getRemoteAddr(); Bucket bucket = buckets.computeIfAbsent(clientIp, this::newBucket); if (bucket.tryConsume(1)) { chain.doFilter(request, response); } else { httpResponse.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS); httpResponse.getWriter().write("Too many requests"); } } private Bucket newBucket(String clientIp) { return Bucket4j.builder() .addLimit(Bandwidth.classic(60, Refill.greedy(60, Duration.ofMinutes(1)))) .build(); } @Override public void destroy() { // 銷(xiāo)毀過(guò)濾器 } }
然后,在Spring Boot應(yīng)用的配置類(lèi)中注冊(cè)這個(gè)過(guò)濾器:
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<Bucket4jRateLimitingFilter> loggingFilter(){ FilterRegistrationBean<Bucket4jRateLimitingFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new Bucket4jRateLimitingFilter()); registrationBean.addUrlPatterns("/api/*"); return registrationBean; } }
最佳實(shí)踐
選擇合適的限流算法
- 令牌桶算法:適用于需要平滑突發(fā)流量的場(chǎng)景。
- 漏桶算法:適用于需要嚴(yán)格控制流量的場(chǎng)景。
- 固定窗口計(jì)數(shù)器:適用于對(duì)簡(jiǎn)單限流要求的場(chǎng)景。
- 滑動(dòng)窗口計(jì)數(shù)器:適用于需要精確控制限流的場(chǎng)景。
優(yōu)化性能
- 減少鎖競(jìng)爭(zhēng):在高并發(fā)環(huán)境下,盡量減少鎖的使用,可以采用無(wú)鎖數(shù)據(jù)結(jié)構(gòu)或者線(xiàn)程安全的數(shù)據(jù)結(jié)構(gòu)。
- 緩存結(jié)果:對(duì)于頻繁訪(fǎng)問(wèn)的數(shù)據(jù),可以考慮進(jìn)行緩存,減少數(shù)據(jù)庫(kù)查詢(xún)的次數(shù)。
- 異步處理:對(duì)于耗時(shí)的操作,可以考慮采用異步處理,提高系統(tǒng)的響應(yīng)速度。
記錄日志和監(jiān)控
- 記錄訪(fǎng)問(wèn)日志:記錄每次接口訪(fǎng)問(wèn)的詳細(xì)信息,包括請(qǐng)求時(shí)間、IP地址、請(qǐng)求路徑等。
- 監(jiān)控限流情況:對(duì)限流情況進(jìn)行監(jiān)控,及時(shí)發(fā)現(xiàn)和處理異常流量。
- 報(bào)警機(jī)制:設(shè)置限流報(bào)警機(jī)制,當(dāng)流量超過(guò)預(yù)設(shè)閾值時(shí),及時(shí)報(bào)警。
總結(jié)
本文詳細(xì)介紹了在Spring Boot中實(shí)現(xiàn)接口訪(fǎng)問(wèn)頻率限制的幾種方法,包括基于過(guò)濾器、攔截器和第三方庫(kù)Bucket4j的實(shí)現(xiàn)。通過(guò)合理的限流策略,可以有效防止惡意攻擊,提升系統(tǒng)的穩(wěn)定性和用戶(hù)體驗(yàn)。在實(shí)際應(yīng)用中,選擇合適的限流算法和實(shí)現(xiàn)方式,并結(jié)合業(yè)務(wù)需求進(jìn)行優(yōu)化,是確保系統(tǒng)高效運(yùn)行的關(guān)鍵。
以上就是SpringBoot接口訪(fǎng)問(wèn)頻率限制的實(shí)現(xiàn)方式的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot接口訪(fǎng)問(wèn)頻率限制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring框架基于AOP實(shí)現(xiàn)簡(jiǎn)單日志管理步驟解析
這篇文章主要介紹了Spring框架基于AOP實(shí)現(xiàn)簡(jiǎn)單日志管理步驟解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Java8中Optional操作的實(shí)際應(yīng)用
Optional類(lèi)是一個(gè)可以為null的容器對(duì)象,如果值存在則isPresent()方法會(huì)返回true,調(diào)用get()方法會(huì)返回該對(duì)象,下面這篇文章主要給大家介紹了關(guān)于Java8中Optional操作實(shí)際應(yīng)用的相關(guān)資料,需要的朋友可以參考下2022-02-02LoggingEventAsyncDisruptorAppender類(lèi)執(zhí)行流程源碼解讀
這篇文章主要介紹了LoggingEventAsyncDisruptorAppender類(lèi)執(zhí)行流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12java實(shí)現(xiàn)給圖片加鋪滿(mǎn)的網(wǎng)格式文字水印
這篇文章主要給大家介紹了關(guān)于java實(shí)現(xiàn)給圖片加鋪滿(mǎn)的網(wǎng)格式文字水印的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01引入QQ郵箱發(fā)送驗(yàn)證碼進(jìn)行安全校驗(yàn)功能實(shí)現(xiàn)
最近遇到這樣的需求用戶(hù)輸入自己的郵箱,點(diǎn)擊獲取驗(yàn)證碼,后臺(tái)會(huì)發(fā)送一封郵件到對(duì)應(yīng)郵箱中,怎么實(shí)現(xiàn)呢?下面小編給大家?guī)?lái)了引入QQ郵箱發(fā)送驗(yàn)證碼進(jìn)行安全校驗(yàn)功能,需要的朋友可以參考下2023-02-02深入了解JAVA數(shù)據(jù)類(lèi)型與運(yùn)算符
這篇文章主要介紹了Java基本數(shù)據(jù)類(lèi)型和運(yùn)算符,結(jié)合實(shí)例形式詳細(xì)分析了java基本數(shù)據(jù)類(lèi)型、數(shù)據(jù)類(lèi)型轉(zhuǎn)換、算術(shù)運(yùn)算符、邏輯運(yùn)算符等相關(guān)原理與操作技巧,需要的朋友可以參考下2021-07-07