Spring?Boot?實(shí)現(xiàn)?IP?限流(保障系統(tǒng)穩(wěn)定性的關(guān)鍵技術(shù))
一、引言
在高并發(fā)的互聯(lián)網(wǎng)應(yīng)用中,系統(tǒng)穩(wěn)定性面臨嚴(yán)峻挑戰(zhàn)。惡意攻擊、爬蟲(chóng)、以及不合理的接口調(diào)用都可能導(dǎo)致系統(tǒng)資源耗盡,影響正常用戶(hù)體驗(yàn)。為了保障系統(tǒng)的穩(wěn)定性和可用性,對(duì)請(qǐng)求進(jìn)行限流是至關(guān)重要的技術(shù)手段。本文將深入探討如何在 Spring Boot 中實(shí)現(xiàn) IP 限流,包括其原理、使用場(chǎng)景、優(yōu)缺點(diǎn)、多種實(shí)現(xiàn)方式,并給出完整的代碼案例,助您構(gòu)建更加健壯的應(yīng)用。
二、IP 限流原理:深入解析
IP 限流,顧名思義,就是根據(jù)客戶(hù)端的 IP 地址對(duì)請(qǐng)求進(jìn)行頻率限制。其核心在于控制每個(gè) IP 地址在單位時(shí)間內(nèi)允許訪問(wèn)的次數(shù),從而避免單個(gè) IP 大量占用資源,保證系統(tǒng)整體的公平性。以下介紹兩種常用的限流算法:
2.1 令牌桶算法 (Token Bucket)
- 工作機(jī)制:
- 系統(tǒng)以恒定速率 (
r) 向令牌桶中放入令牌。 - 每個(gè)請(qǐng)求需要從令牌桶中獲取一個(gè)令牌才能被處理。
- 如果令牌桶為空,則請(qǐng)求被拒絕或等待。
- 令牌桶具有最大容量 (
b),當(dāng)令牌數(shù)量達(dá)到上限時(shí),新添加的令牌會(huì)被丟棄。
- 系統(tǒng)以恒定速率 (
- 優(yōu)點(diǎn):
- 平滑突發(fā)流量: 允許短時(shí)間內(nèi)突發(fā)大量請(qǐng)求,因?yàn)榱钆仆爸锌赡芊e累了足夠的令牌。
- 容錯(cuò)性高: 即使短時(shí)間內(nèi)沒(méi)有請(qǐng)求,令牌也會(huì)持續(xù)積累,允許后續(xù)的請(qǐng)求 burst。
- 動(dòng)態(tài)適應(yīng): 更適合處理動(dòng)態(tài)變化的請(qǐng)求,可以在一定程度上應(yīng)對(duì)流量高峰。
2.2 漏桶算法 (Leaky Bucket)
- 工作機(jī)制:
- 請(qǐng)求被放入漏桶中,漏桶以恒定速率 (
r) 處理請(qǐng)求。 - 當(dāng)請(qǐng)求的速率超過(guò)漏桶的處理能力時(shí),多余的請(qǐng)求會(huì)在桶中堆積。
- 如果桶滿,則新的請(qǐng)求會(huì)被丟棄。
- 請(qǐng)求被放入漏桶中,漏桶以恒定速率 (
- 優(yōu)點(diǎn):
- 嚴(yán)格控制速率: 能?chē)?yán)格控制輸出速率,保證請(qǐng)求以固定速度被處理。
- 平滑輸出: 可以將突發(fā)流量轉(zhuǎn)化為平滑的輸出流量。
- 缺點(diǎn):
- 難以應(yīng)對(duì)突發(fā)流量: 即使系統(tǒng)資源充足,也無(wú)法快速處理突發(fā)請(qǐng)求,容易導(dǎo)致請(qǐng)求被拒絕。
2.3 令牌桶與漏桶算法對(duì)比
| 特性 | 令牌桶算法 | 漏桶算法 |
|---|---|---|
| 流量控制 | 允許突發(fā)流量,平均速率限制 | 嚴(yán)格控制速率,平滑輸出 |
| 適用場(chǎng)景 | 需要容忍一定突發(fā)流量的場(chǎng)景,如 API 限流 | 對(duì)速率有嚴(yán)格要求的場(chǎng)景,如消息隊(duì)列削峰填谷 |
| 實(shí)現(xiàn)復(fù)雜度 | 相對(duì)簡(jiǎn)單 | 相對(duì)簡(jiǎn)單 |
| 動(dòng)態(tài)調(diào)整 | 容易實(shí)現(xiàn)動(dòng)態(tài)調(diào)整令牌生成速率和桶容量 | 調(diào)整輸出速率和桶容量相對(duì)困難 |
三、IP 限流的使用場(chǎng)景:應(yīng)用廣泛
IP 限流作為一種基礎(chǔ)且有效的限流手段,在各種場(chǎng)景中都有廣泛的應(yīng)用:
3.1 API 網(wǎng)關(guān)限流
在 API 網(wǎng)關(guān)層實(shí)現(xiàn) IP 限流,可以統(tǒng)一管理和控制所有 API 的請(qǐng)求頻率,是微服務(wù)架構(gòu)中保護(hù)后端服務(wù)的關(guān)鍵措施。
3.2 Web 應(yīng)用安全防護(hù)
對(duì)登錄、注冊(cè)、找回密碼等敏感接口進(jìn)行 IP 限流,防止暴力 破解和惡意注冊(cè),增強(qiáng)應(yīng)用的安全性。
3.3 微服務(wù)架構(gòu)
在微服務(wù)架構(gòu)中,對(duì)每個(gè)微服務(wù)的接口進(jìn)行 IP 限流,保障服務(wù)的穩(wěn)定性和可用性,防止服務(wù)雪崩。
3.4 防止惡意爬蟲(chóng)
限制爬蟲(chóng)程序的訪問(wèn)頻率,防止爬蟲(chóng)過(guò)度抓取數(shù)據(jù),影響正常用戶(hù)體驗(yàn)。
3.5 控制資源使用
在共享資源的系統(tǒng)中,限制單個(gè) IP 地址的資源使用,確保每個(gè)用戶(hù)都能公平地使用資源。
四、IP 限流的優(yōu)缺點(diǎn)分析:理性看待
4.1 優(yōu)點(diǎn)
- 精準(zhǔn)控制: 可以針對(duì)每個(gè) IP 地址進(jìn)行精確的請(qǐng)求限制,有效防止單個(gè) IP 的過(guò)度請(qǐng)求。
- 實(shí)現(xiàn)簡(jiǎn)單: 相比于其他復(fù)雜的限流策略,IP 限流的實(shí)現(xiàn)相對(duì)簡(jiǎn)單,不需要復(fù)雜的算法和配置。
- 易于維護(hù): 由于每個(gè) IP 的限流規(guī)則相對(duì)獨(dú)立,維護(hù)和管理起來(lái)比較方便。
4.2 缺點(diǎn)
- IP 偽裝繞過(guò): 惡意攻擊者可以通過(guò) IP 代理或 VPN 等方式偽裝自己的 IP 地址,繞過(guò) IP 限流的限制。
- 影響正常用戶(hù): 在某些情況下,正常用戶(hù)可能會(huì)因?yàn)榫W(wǎng)絡(luò)環(huán)境等原因被錯(cuò)誤地限流,影響用戶(hù)體驗(yàn)。
- IP 地址池問(wèn)題: 如果多個(gè)用戶(hù)共享同一個(gè)公網(wǎng) IP 地址,可能會(huì)導(dǎo)致正常用戶(hù)的請(qǐng)求被錯(cuò)誤地限流。
- 無(wú)法區(qū)分用戶(hù): IP 限流只能根據(jù) IP 地址進(jìn)行限制,無(wú)法區(qū)分不同的用戶(hù),可能導(dǎo)致惡意用戶(hù)利用合法用戶(hù)的 IP 地址發(fā)起攻擊。
- 不能應(yīng)對(duì)分布式攻擊: 對(duì)于分布式拒絕服務(wù)(DDoS)攻擊,IP 限流只能對(duì)單個(gè) IP 進(jìn)行限制,無(wú)法有效應(yīng)對(duì)大量不同 IP 地址的攻擊。
- 維護(hù)成本: 如果需要對(duì)大量 IP 地址進(jìn)行限流,維護(hù)成本可能會(huì)比較高,需要考慮使用更高效的存儲(chǔ)方案,例如 Redis 或數(shù)據(jù)庫(kù)。
五、Spring Boot 實(shí)現(xiàn) IP 限流:代碼案例
以下提供兩種基于 Spring Boot 的 IP 限流實(shí)現(xiàn)方式:
5.1 基于 Guava RateLimiter 的 IP 限流過(guò)濾器(推薦)
這種方式使用 Guava 的 RateLimiter 實(shí)現(xiàn)令牌桶算法,簡(jiǎn)單高效,易于配置。
1. 項(xiàng)目搭建:
創(chuàng)建一個(gè) Spring Boot 項(xiàng)目,并添加以下依賴(lài):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
</dependencies>2. 創(chuàng)建 RateLimiterService:
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.RateLimiter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RateLimiterService {
private static final Logger logger = LoggerFactory.getLogger(RateLimiterService.class);
@Value("${rate.limit.permitsPerSecond:10}") // 默認(rèn)值 10
private double permitsPerSecond;
// 使用 LoadingCache 緩存每個(gè) IP 的 RateLimiter
private final LoadingCache<String, RateLimiter> rateLimiterCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES) // 緩存 1 分鐘
.build(new CacheLoader<String, RateLimiter>() {
@Override
public RateLimiter load(String ip) {
logger.info("Creating RateLimiter for IP: {}, rate: {}", ip, permitsPerSecond);
return RateLimiter.create(permitsPerSecond);
}
});
/**
* 嘗試獲取令牌
* @param ip 客戶(hù)端 IP 地址
* @return true: 允許訪問(wèn), false: 限流
*/
public boolean tryAcquire(String ip) {
RateLimiter rateLimiter = rateLimiterCache.getUnchecked(ip);
return rateLimiter.tryAcquire();
}
}3. 創(chuàng)建 IP 限流過(guò)濾器:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class IpRateLimitFilter extends OncePerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(IpRateLimitFilter.class);
@Autowired
private RateLimiterService rateLimiterService;
@Value("${rate.limit.enabled:true}")
private boolean rateLimitEnabled;
@Value("${rate.limit.excludePatterns:}") // 排除的URL,用逗號(hào)分隔
private String excludePatterns;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (!rateLimitEnabled) {
filterChain.doFilter(request, response);
return; // 如果禁用限流,直接放行
}
// 排除不需要限流的URL
String requestURI = request.getRequestURI();
if (excludePatterns != null && !excludePatterns.isEmpty()) {
String[] patterns = excludePatterns.split(",");
for (String pattern : patterns) {
if (requestURI.startsWith(pattern.trim())) {
filterChain.doFilter(request, response);
return;
}
}
}
String clientIp = getClientIp(request);
if (rateLimiterService.tryAcquire(clientIp)) {
filterChain.doFilter(request, response); // 允許訪問(wèn)
} else {
logger.warn("Rate limit exceeded for IP: {}", clientIp);
response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
response.getWriter().write("Too many requests from this IP.");
}
}
private String getClientIp(HttpServletRequest request) {
String xffHeader = request.getHeader("X-Forwarded-For");
if (xffHeader == null) {
return request.getRemoteAddr();
}
return xffHeader.split(",")[0];
}
}4. 配置過(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<IpRateLimitFilter> ipRateLimitFilterRegistration(IpRateLimitFilter ipRateLimitFilter) {
FilterRegistrationBean<IpRateLimitFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(ipRateLimitFilter);
registration.addUrlPatterns("/*"); // 對(duì)所有請(qǐng)求進(jìn)行限流
registration.setOrder(1);
return registration;
}
}5. 創(chuàng)建測(cè)試控制器:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public String test() {
return "Hello, this is a test API.";
}
}6. application.properties 配置:
# 限流配置 rate.limit.enabled=true rate.limit.permitsPerSecond=10 rate.limit.excludePatterns=/health,/metrics # 排除的URL
5.2 基于 ConcurrentHashMap 的 IP 限流
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class IPRequestRateLimiter implements HandlerInterceptor {
private final int REQUEST_LIMIT = 10; // 每秒最大請(qǐng)求數(shù)
private final Map<String, AtomicInteger> requestCountsPerIP = new ConcurrentHashMap<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ipAddress = request.getRemoteAddr();
// 1. 初始化計(jì)數(shù)器
requestCountsPerIP.computeIfAbsent(ipAddress, k -> new AtomicInteger(0));
// 2. 檢查請(qǐng)求是否超限
if (requestCountsPerIP.get(ipAddress).incrementAndGet() > REQUEST_LIMIT) {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write("Too many requests from this IP. Please try again later.");
return false; // 阻止請(qǐng)求
}
return true; // 允許請(qǐng)求
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String ipAddress = request.getRemoteAddr();
// 3. 請(qǐng)求完成后,減少計(jì)數(shù)
requestCountsPerIP.get(ipAddress).decrementAndGet();
}
}注意: 此方式只是一個(gè)簡(jiǎn)易的實(shí)現(xiàn),存在時(shí)間窗口不精準(zhǔn)等問(wèn)題,生產(chǎn)環(huán)境不建議使用.
5.3 測(cè)試
啟動(dòng) Spring Boot 項(xiàng)目,使用工具(如 Postman)模擬不同 IP 地址的請(qǐng)求。當(dāng)某個(gè) IP 的請(qǐng)求頻率超過(guò)配置的限制時(shí),會(huì)收到 Too many requests from this IP. 的響應(yīng)。
六、進(jìn)階特性:提升 IP 限流的可靠性
- 動(dòng)態(tài)調(diào)整限流規(guī)則: 可以根據(jù)系統(tǒng)負(fù)載情況動(dòng)態(tài)調(diào)整
permitsPerSecond的值,實(shí)現(xiàn)更靈活的限流策略??梢酝ㄟ^(guò) Spring Cloud Config 等配置中心實(shí)現(xiàn)動(dòng)態(tài)更新。 - 黑名單和白名單: 支持黑名單和白名單 IP 地址,對(duì)特定的 IP 地址進(jìn)行特殊處理。可以使用 Redis 等緩存存儲(chǔ)黑白名單,并定期更新。
- 監(jiān)控和告警: 集成監(jiān)控系統(tǒng)(如 Prometheus、Grafana),實(shí)時(shí)監(jiān)控 IP 限流的效果,并設(shè)置告警規(guī)則,及時(shí)發(fā)現(xiàn)異常情況。
- 分布式限流: 對(duì)于分布式系統(tǒng),需要使用分布式鎖或 Redis 等技術(shù)實(shí)現(xiàn)分布式限流,保證所有節(jié)點(diǎn)使用相同的限流規(guī)則。
- 更精細(xì)化的限流維度: 可以結(jié)合用戶(hù) ID、API 接口等維度進(jìn)行更精細(xì)化的限流,例如限制每個(gè)用戶(hù)每天可以訪問(wèn)某個(gè) API 接口的次數(shù)。
七、總結(jié)
在 Spring Boot 中實(shí)現(xiàn) IP 限流是一種簡(jiǎn)單而有效的方式來(lái)保障系統(tǒng)的穩(wěn)定性和可用性。通過(guò)本文的詳細(xì)介紹和代碼案例,相信您已經(jīng)掌握了 IP 限流的基本原理和實(shí)現(xiàn)方式。在實(shí)際應(yīng)用中,請(qǐng)根據(jù)您的具體需求選擇合適的限流算法和實(shí)現(xiàn)方式,并結(jié)合其他安全措施,構(gòu)建更加健壯的系統(tǒng)。請(qǐng)記住,安全策略應(yīng)該是全面的,限流只是其中一部分。
到此這篇關(guān)于Spring Boot 實(shí)現(xiàn) IP 限流(保障系統(tǒng)穩(wěn)定性的關(guān)鍵技術(shù))的文章就介紹到這了,更多相關(guān)Spring Boot IP 限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot使用Redis對(duì)用戶(hù)IP進(jìn)行接口限流的項(xiàng)目實(shí)踐
- SpringBoot使用Redis對(duì)用戶(hù)IP進(jìn)行接口限流的示例詳解
- SpringBoot+Resilience4j實(shí)現(xiàn)接口限流的示例代碼
- SpringBoot整合Redis并且用Redis實(shí)現(xiàn)限流的方法 附Redis解壓包
- SpringBoot高并發(fā)下控制限流的幾種實(shí)現(xiàn)方法
- Spring Boot集成Resilience4J實(shí)現(xiàn)限流/重試/隔離
- Spring Boot使用Redisson實(shí)現(xiàn)滑動(dòng)窗口限流的項(xiàng)目實(shí)踐
相關(guān)文章
java正則表達(dá)式對(duì)特殊字符的轉(zhuǎn)義以及異?,F(xiàn)象
這篇文章主要給大家介紹了關(guān)于java正則表達(dá)式對(duì)特殊字符的轉(zhuǎn)義以及異?,F(xiàn)象的相關(guān)資料,在Java中使用正則表達(dá)式匹配特殊字符也需要使用轉(zhuǎn)義字符"\",需要的朋友可以參考下2023-06-06
一篇文章告訴你JAVA Mybatis框架的核心原理到底有多重要
yBatis的底層操作封裝了JDBC的API,MyBatis的工作原理以及核心流程與JDBC的使用步驟一脈相承,MyBatis的核心對(duì)象(SqlSession,Executor)與JDBC的核心對(duì)象(Connection,Statement)相互對(duì)應(yīng)2021-06-06
SpringBoot實(shí)現(xiàn)yml配置文件為變量賦值
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)yml配置文件為變量賦值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
mybatis多對(duì)多關(guān)聯(lián)實(shí)戰(zhàn)教程(推薦)
下面小編就為大家?guī)?lái)一篇mybatis多對(duì)多關(guān)聯(lián)實(shí)戰(zhàn)教程(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
Springboot項(xiàng)目監(jiān)聽(tīng)器失效問(wèn)題解決
這篇文章主要介紹了Springboot項(xiàng)目監(jiān)聽(tīng)器失效問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
Spring + Mybatis 項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源實(shí)例詳解
這篇文章主要介紹了Spring + Mybatis 項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源的相關(guān)資料,需要的朋友參考下吧2017-04-04
Java高效實(shí)現(xiàn)PDF與OFD的相互轉(zhuǎn)換的示例代碼
在數(shù)字化辦公日益普及的今天,文檔格式的標(biāo)準(zhǔn)化與跨平臺(tái)兼容性成為企業(yè)和開(kāi)發(fā)者面臨的重要挑戰(zhàn),OFD作為國(guó)家標(biāo)準(zhǔn),在政務(wù)、金融等領(lǐng)域具有不可替代的地位,而PDF則以其廣泛的兼容性成為日常交流的首選本文將深入探討如何高效地實(shí)現(xiàn)PDF與OFD的相互轉(zhuǎn)換2025-10-10
SpringBoot整合Shiro靜態(tài)資源攔截配置實(shí)踐
本文介紹了Spring?Boot整合Thymeleaf訪問(wèn)resources目錄下static靜態(tài)資源的方法,并提供三種解決方式,方法一通過(guò)修改訪問(wèn)路徑,方法二在yml文件中修改配置,方法三通過(guò)創(chuàng)建statics目錄并調(diào)整攔截配置來(lái)實(shí)現(xiàn)2026-05-05

