根據對方IP地址來限制接口的QPS(每秒查詢率),你可以結合Spring Boot應用、Guava的RateLimiter或者自定義的并發控制邏輯來實現。以下是一個基于Guava RateLimiter和Spring Boot的示例,展示如何根據IP地址來限制接口的QPS:
- 添加Guava依賴
首先,確保你的pom.xml
文件中包含了Guava的依賴。
- 創建RateLimiter存儲
你可以使用ConcurrentHashMap
來存儲每個IP地址對應的RateLimiter。
import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.ConcurrentHashMap;public class IpRateLimiter {private final ConcurrentHashMap<String, RateLimiter> ipRateLimiterMap = new ConcurrentHashMap<>();private final double rate; // 例如:每秒允許的請求數public IpRateLimiter(double rate) {this.rate = rate;}public synchronized RateLimiter getRateLimiter(String ip) {return ipRateLimiterMap.computeIfAbsent(ip, k -> RateLimiter.create(rate));}
}
- 在Controller中使用RateLimiter
在Controller中,你可以從請求中獲取IP地址,并使用IpRateLimiter
來獲取對應的RateLimiter,然后檢查是否允許請求通過。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;@RestController
public class MyController {@Autowiredprivate IpRateLimiter ipRateLimiter;@GetMapping("/limited-endpoint")public String limitedEndpoint(HttpServletRequest request) {String ipAddress = request.getRemoteAddr(); // 簡化的IP獲取方式,可能需要根據實際情況調整RateLimiter rateLimiter = ipRateLimiter.getRateLimiter(ipAddress);if (!rateLimiter.tryAcquire()) {// 如果無法獲取許可,則返回錯誤信息或執行其他邏輯return "Too Many Requests from IP: " + ipAddress + ", please try again later.";}// 如果可以獲取許可,則處理請求并返回結果return "Success";}
}
注意:上面的request.getRemoteAddr()
方法可能只返回內網IP(如果請求經過反向代理),你可能需要配置X-Forwarded-For頭來獲取真實的客戶端IP。
- 配置X-Forwarded-For
如果你的應用部署在反向代理(如Nginx)后面,你需要確保反向代理正確地設置了X-Forwarded-For頭,并且你的Spring Boot應用能夠解析這個頭來獲取真實的客戶端IP。
- 清理RateLimiter
由于RateLimiter實例在ipRateLimiterMap
中會一直存在,直到應用重啟,你可能需要實現一種機制來清理長時間沒有活動的RateLimiter實例,以避免內存泄漏。這可以通過定期掃描并移除長時間未使用的RateLimiter來實現。
- 其他考慮
- 如果有大量不同的IP地址訪問你的接口,
ipRateLimiterMap
可能會變得非常大。你可能需要考慮如何限制其大小或實現一種緩存淘汰策略。 - 在生產環境中,你可能還需要考慮如何優雅地處理被限流的請求,例如返回特定的HTTP狀態碼、記錄日志等。