Spring Boot 接口限流全攻略:注解 + 令牌桶 + 用戶/IP/設備維度限流一網打盡!
限流,從“救火”到“預防性策略”
在高并發環境下,服務不怕請求多,怕的是毫無防線地“裸奔”。限流機制正是你系統的第一道防火墻,它不是阻礙增長的絆腳石,而是保障可持續運行的關鍵策略。
本文將從注解 + AOP 切入,擴展出支持用戶、IP、設備 ID 維度的客體級限流系統,核心基于 Guava 提供的 RateLimiter 實現 令牌桶算法,以一種高性能、非侵入、易配置的方式守護你的接口安全。
路徑結構與包名規劃
/src/main
└── /java
└── /com
└── /icoderoad
├── /annotation // 限流注解定義
├── /aspect // AOP 限流切面
├── /controller // 控制器示例
└── /utils // 工具類(獲取用戶/IP/設備信息)限流場景概覽與算法回顧
限流的目的是防止系統資源被過度消耗,從而保障整體服務穩定。
常見限流維度
維度類型 | 說明 |
接口級別限流 | 限制某接口總訪問頻率 |
用戶級別限流 | 同一用戶請求頻率控制 |
IP 限流 | 同一 IP 單位時間內請求數限制 |
設備 ID 限流 | 依據設備號或終端號限流,常用于移動端 |
支持的限流算法
- 固定窗口算法
- 滑動窗口算法
- 漏桶算法
- 令牌桶算法(本實現采用)
限流實現方案詳解(支持用戶/IP/設備)
注解定義
路徑:
/*/src/main/java/com/icoderoad/annotation/RateLimit.java*/
package com.icoderoad.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
int count() default 100; // 每 time 秒允許請求次數
int time() default 1; // 時間窗口長度(秒)
boolean perUser() default false; // 是否按用戶限流
boolean perIp() default false; // 是否按IP限流
boolean perDevice() default false; // 是否按設備ID限流
}工具類:統一抽取用戶/IP/設備信息
路徑:
/*/src/main/java/com/icoderoad/utils/RequestUtils.java*/
package com.icoderoad.utils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.*;
public class RequestUtils {
public static HttpServletRequest getRequest() {
RequestAttributes attr = RequestContextHolder.getRequestAttributes();
return attr == null ? null : ((ServletRequestAttributes) attr).getRequest();
}
public static String getUserId() {
// 模擬獲取,實際項目應取登錄上下文、JWT 等
return "user-123";
}
public static String getIpAddr() {
HttpServletRequest request = getRequest();
if (request == null) return "unknown";
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
public static String getDeviceId() {
HttpServletRequest request = getRequest();
if (request == null) return "device-unknown";
// 假設設備ID在請求頭中,如 `Device-Id`
String deviceId = request.getHeader("Device-Id");
return (deviceId == null || deviceId.isEmpty()) ? "device-unknown" : deviceId;
}
}AOP 切面實現(動態生成維度Key)
路徑:
/*/src/main/java/com/icoderoad/aspect/RateLimitAspect.java*/
package com.icoderoad.aspect;
import com.google.common.util.concurrent.RateLimiter;
import com.icoderoad.annotation.RateLimit;
import com.icoderoad.utils.RequestUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Aspect
@Component
public class RateLimitAspect {
private final Map<String, RateLimiter> limiterPool = new ConcurrentHashMap<>();
@Before("@annotation(rateLimit)")
public void applyRateLimit(JoinPoint joinPoint, RateLimit rateLimit) {
String baseKey = joinPoint.getSignature().toLongString();
StringBuilder keyBuilder = new StringBuilder(baseKey);
if (rateLimit.perUser()) {
keyBuilder.append(":USER:").append(RequestUtils.getUserId());
}
if (rateLimit.perIp()) {
keyBuilder.append(":IP:").append(RequestUtils.getIpAddr());
}
if (rateLimit.perDevice()) {
keyBuilder.append(":DEVICE:").append(RequestUtils.getDeviceId());
}
String key = keyBuilder.toString();
RateLimiter rateLimiter = limiterPool.computeIfAbsent(
key,
k -> RateLimiter.create(rateLimit.count() / (double) rateLimit.time())
);
if (!rateLimiter.tryAcquire()) {
throw new RuntimeException("訪問過于頻繁,請稍后再試");
}
}
}控制器示例
路徑:
/*/src/main/java/com/icoderoad/controller/TestController.java*/
package com.icoderoad.controller;
import com.icoderoad.annotation.RateLimit;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/test")
public class TestController {
// 接口全局限流,每秒最多10次
@RateLimit(count = 10, time = 1)
@GetMapping("/global")
public String globalLimit() {
return "接口級別限流成功";
}
// 用戶維度限流,每用戶每秒最多5次
@RateLimit(count = 5, time = 1, perUser = true)
@GetMapping("/user")
public String userLimit() {
return "用戶維度限流成功";
}
// IP 限流:每個IP每秒最多3次
@RateLimit(count = 3, time = 1, perIp = true)
@GetMapping("/ip")
public String ipLimit() {
return "IP維度限流成功";
}
// 設備ID限流:每設備每秒最多2次
@RateLimit(count = 2, time = 1, perDevice = true)
@GetMapping("/device")
public String deviceLimit() {
return "設備維度限流成功";
}
// 綜合限流:用戶 + IP + 設備 多維組合限流
@RateLimit(count = 1, time = 1, perUser = true, perIp = true, perDevice = true)
@GetMapping("/all")
public String combinedLimit() {
return "多維度限流成功";
}
}技術方案總結
特性 | 描述 |
非侵入式設計 | 通過注解和 AOP 實現,與業務代碼解耦 |
多維度支持 | 支持接口級、用戶級、IP、設備ID限流 |
動態速率配置 | 注解級別自由配置 count/time 參數 |
高性能內存實現 | 基于 Guava 的 RateLimiter,納秒級響應 |
容易擴展 | 可擴展 Redis 分布式限流、API分組限流、黑名單機制等 |
結語:限流機制的本質是“可控增長”
在系統架構中,限流從來都不是“封鎖通道”,而是提供有序釋放系統能力的護欄。
當你掌握了如本文所述的用戶/IP/設備維度限流機制之后,你將具備:
- 對接口訪問的精細化調控能力
- 面對突發高峰的穩定服務能力
- 對系統濫用行為的有效應對手段




























