首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring Boot 自定義註解實現接口限流:AOP + 本地緩存的輕量級方案

     編輯:Java 相關知識

場景與目標

防止惡意刷單、爬蟲、DDoS 等高頻調用,需在控制器方法級別聲明限流規則,例如「同一 IP 5 秒內最多 10 次」。本文實現基於 AOP 的 @RequestLimit 註解,採用本地 HashMap 存儲計數器,適合單體/小規模微服務;分布式場景請改用 Redis + Lua 腳本。

1. 定義限流註解

2. 異常類

public class RequestLimitException extends RuntimeException {
    public RequestLimitException() { super("請求頻率超過限制"); }
    public RequestLimitException(String msg) { super(msg); }
}

3. 切面實現(核心邏輯)

@Aspect
@Component
public class RequestLimitAspect {
    private static final Logger log = LoggerFactory.getLogger(RequestLimitAspect.class);
    // Key: "req_limit_" + URL + IP
    private final Map<String, Integer> counter = new ConcurrentHashMap<>();

    @Before("@annotation(limit) && within(@org.springframework.stereotype.Controller *)")
    public void doLimit(JoinPoint jp, RequestLimit limit) {
        HttpServletRequest req = extractRequest(jp);
        if (req == null) throw new RequestLimitException("缺少 HttpServletRequest 參數");

        String key = "req_limit_" + req.getRequestURL() + "_" + req.getRemoteAddr();
        int current = counter.compute(key, (k, v) -> v == null ? 1 : v + 1);

        // 首次請求啟動定時清理任務
        if (current == 1) {
            new Timer().schedule(new TimerTask() {
                @Override public void run() { counter.remove(key); }
            }, limit.time());
        }

        if (current > limit.count()) {
            log.warn("IP [{}] 訪問 [{}] 超過限制 {} 次/{}ms", 
                     req.getRemoteAddr(), req.getRequestURL(), limit.count(), limit.time());
            throw new RequestLimitException();
        }
    }

    private HttpServletRequest extractRequest(JoinPoint jp) {
        return Arrays.stream(jp.getArgs())
                     .filter(HttpServletRequest.class::isInstance)
                     .map(HttpServletRequest.class::cast)
                     .findFirst().orElse(null);
    }
}

4. 控制器使用示例

5. 全局異常處理(返回友好 JSON)

handleLimit(RequestLimitException e) { return Map.of("code", 429, "msg", e.getMessage(), "retryAfter", "5s"); } }

局限性與擴展方向

局限擴展方案
單機 HashMap,重啟丟失、多實例不共享改用 Redis INCR + EXPIRE 或 Redisson RateLimiter
Timer 任務積累可能導致 OOM改用 ScheduledExecutorService 或 Caffeine 過期策略
僅支援 Controller 方法,Filter/Interceptor 層面無效實現 OncePerRequestFilter 統一攔截所有入口
無區分用戶維度(僅 IP)結合 JWT Token 解析 userId 作為 Key 一部分

生產級替代方案推薦

  • Sentinel:阿里開源,支援熱點參數限流、熔斷降級、控制台實時監控;
  • Resilience4j:輕量、函數式、Spring Boot 2/3 原生整合;
  • Gateway + Redis RateLimiter:網關層統一治理,零業務侵入。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved