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ù)器的單機限流?
?核心邏輯?
- ?攔截器?:使用
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)計當前窗口請求數(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)點? | ?缺點? |
|---|---|---|---|
| Redis + Lua | 分布式環(huán)境,高精度限流 | 原子性操作,支持分布式,可動態(tài)調(diào)整參數(shù) | 依賴Redis,網(wǎng)絡(luò)開銷較大 |
| 內(nèi)存計數(shù)器 | 單機環(huán)境,簡單場景 | 無外部依賴,實現(xiàn)簡單 | 不支持分布式,重啟后數(shù)據(jù)丟失 |
?擴展建議?:
- ?動態(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)文章
關(guān)于IDEA2020.1新建項目maven PKIX 報錯問題解決方法
這篇文章主要介紹了關(guān)于IDEA2020.1新建項目maven PKIX 報錯問題解決方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
基于ChatGPT+SpringBoot實現(xiàn)智能聊天AI機器人接口并上線至服務(wù)器的方法
ChatGPT是一款基于自然語言處理技術(shù)的聊天機器人,ChatGPT可以模擬真實的人類對話,并能夠更貼近用戶的需求,提供更有價值的服務(wù),這篇文章主要介紹了基于ChatGPT+SpringBoot實現(xiàn)智能聊天AI機器人接口并上線至服務(wù)器,需要的朋友可以參考下2023-02-02
Java try catch finally的執(zhí)行順序解讀
這篇文章主要介紹了Java try catch finally的執(zhí)行順序,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-05-05
springboot?vue項目管理前后端實現(xiàn)編輯功能
這篇文章主要為大家介紹了springboot?vue項目管理前后端實現(xiàn)編輯功能,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05
使用@Autowired 注入RedisTemplate報錯的問題及解決
這篇文章主要介紹了使用@Autowired 注入RedisTemplate報錯的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08

