SpringBoot通過攔截器實現(xiàn)接口限流的兩種方案
在Spring Boot中,可以通過自定義攔截器(Interceptor)結(jié)合Redis或內(nèi)存計數(shù)器實現(xiàn)接口限流。以下是兩種典型實現(xiàn)方式及代碼示例:
?方案一:基于Redis + Lua腳本的分布式限流?
?核心邏輯?
- ?Redis配置?:使用Lua腳本保證原子性操作(計數(shù)+過期時間設(shè)置)。
- ?攔截器?:攔截請求,通過Redis統(tǒng)計IP或用戶維度的訪問次數(shù)。
- ?注冊攔截器?:指定攔截路徑和排除路徑。
?代碼實現(xiàn)?
1. Redis配置類
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
@Bean
public DefaultRedisScript<Long> rateLimitScript() {
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(
"local key = KEYS[1]\n" +
"local limit = tonumber(ARGV[1])\n" +
"local expire = tonumber(ARGV[2])\n" +
"local current = redis.call('INCR', key)\n" +
"if current == 1 then\n" +
" redis.call('EXPIRE', key, expire)\n" +
"end\n" +
"return current > limit and 1 or 0"
);
script.setResultType(Long.class);
return script;
}
}
2. 限流攔截器
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private DefaultRedisScript<Long> rateLimitScript;
private static final int DEFAULT_LIMIT = 60; // 每分鐘60次
private static final int DEFAULT_TIMEOUT = 60; // 60秒過期
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
String ip = request.getRemoteAddr();
String uri = request.getRequestURI();
String key = "rate_limit:" + ip + ":" + uri.split("/")[1]; // 按接口前綴分組
Long result = redisTemplate.execute(
rateLimitScript,
Collections.singletonList(key),
DEFAULT_LIMIT, DEFAULT_TIMEOUT
);
if (result != null && result == 1) {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write("Too many requests");
return false;
}
return true;
}
}
3. 注冊攔截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private RateLimitInterceptor rateLimitInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(rateLimitInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/login");
}
}
?方案二:基于內(nèi)存計數(shù)器的單機(jī)限流?
?核心邏輯?
- ?攔截器?:使用
ConcurrentHashMap存儲IP和訪問時間戳。 - ?滑動窗口?:統(tǒng)計1分鐘內(nèi)的請求數(shù),超限則拒絕。
?代碼實現(xiàn)?
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 currentTime = System.currentTimeMillis();
// 清理過期請求
requestCounts.entrySet().removeIf(entry ->
currentTime - entry.getValue() > TimeUnit.MINUTES.toMillis(1)
);
// 統(tǒng)計當(dāng)前窗口請求數(shù)
long count = requestCounts.values().stream()
.filter(timestamp -> currentTime - timestamp < TimeUnit.MINUTES.toMillis(1))
.count();
if (count >= ALLOWED_REQUESTS_PER_MINUTE) {
response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
response.getWriter().write("請求過于頻繁");
return false;
}
requestCounts.put(clientIp, currentTime);
return true;
}
}
?關(guān)鍵對比與選擇建議?
| ?方案? | ?適用場景? | ?優(yōu)點(diǎn)? | ?缺點(diǎn)? |
|---|---|---|---|
| Redis + Lua | 分布式環(huán)境,高精度限流 | 原子性操作,支持分布式,可動態(tài)調(diào)整參數(shù) | 依賴Redis,網(wǎng)絡(luò)開銷較大 |
| 內(nèi)存計數(shù)器 | 單機(jī)環(huán)境,簡單場景 | 無外部依賴,實現(xiàn)簡單 | 不支持分布式,重啟后數(shù)據(jù)丟失 |
?擴(kuò)展建議?:
- ?動態(tài)配置?:將限流參數(shù)(如
DEFAULT_LIMIT)改為從配置中心讀取。 - ?注解化?:結(jié)合自定義注解(如
@RateLimit)實現(xiàn)更靈活的限流規(guī)則。
兩種方案均能有效實現(xiàn)接口限流,根據(jù)項目需求選擇即可。
到此這篇關(guān)于SpringBoot通過攔截器實現(xiàn)接口限流的兩種方案的文章就介紹到這了,更多相關(guān)SpringBoot攔截器接口限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springcloud中Feign傳遞參數(shù)的過程解析
這篇文章主要介紹了Springcloud中Feign傳遞參數(shù)的過程,單個參數(shù)的傳值有兩種方式,第一種使用@RequestParam/@PathVariable進(jìn)行傳值,傳遞多個參數(shù):多個參數(shù)的傳值可以使用多個@RequestParam來進(jìn)行傳參,需要的朋友可以參考下2023-09-09
SpringSecurity實現(xiàn)前后端分離登錄token認(rèn)證詳解
目前市面上比較流行的權(quán)限框架主要實Shiro和Spring Security,這兩個框架各自側(cè)重點(diǎn)不同,各有各的優(yōu)劣,本文將給大家詳細(xì)介紹SpringSecurity如何實現(xiàn)前后端分離登錄token認(rèn)證2023-06-06
Java利用Spire.Doc實現(xiàn)XML轉(zhuǎn)PDF的實戰(zhàn)指南
本文介紹了使用Spire.DocforJava將XML轉(zhuǎn)換為PDF的過程,首先說明了XML和PDF的應(yīng)用場景,然后通過Maven引入依賴,核心代碼展示了加載XML并轉(zhuǎn)換為PDF的方法,最后討論了進(jìn)階設(shè)置、注意事項及常見問題,幫助開發(fā)者更好地完成轉(zhuǎn)換任務(wù),需要的朋友可以參考下2026-04-04
SpringBoot+隨機(jī)鹽值+雙重MD5實現(xiàn)加密登錄
數(shù)據(jù)加密在很多項目上都可以用到,大部分都會采用MD5進(jìn)行加密,本文主要介紹了SpringBoot+隨機(jī)鹽值+雙重MD5實現(xiàn)加密登錄,具有一定的參考價值,感興趣的可以了解一下2024-02-02
SpringBoot-JPA刪除不成功,只執(zhí)行了查詢語句問題
這篇文章主要介紹了SpringBoot-JPA刪除不成功,只執(zhí)行了查詢語句問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
java監(jiān)聽器實現(xiàn)在線人數(shù)統(tǒng)計
這篇文章主要為大家詳細(xì)介紹了java監(jiān)聽器實現(xiàn)在線人數(shù)統(tǒng)計,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-11-11
Java實現(xiàn)HTML轉(zhuǎn)Word的完整指南
在Java應(yīng)用程序中處理文檔轉(zhuǎn)換時,經(jīng)常需要將HTML內(nèi)容精準(zhǔn)導(dǎo)出為格式規(guī)范的Word文檔,通過Spire.Doc for Java庫,開發(fā)者可以輕松實現(xiàn)HTML到Word的高保真轉(zhuǎn)換,所以本文給大家介紹了Java實現(xiàn)HTML轉(zhuǎn)Word的完整指南,需要的朋友可以參考下2025-09-09

