本文探討在高併發 Servlet 應用中,如何通過請求頻率控制保護數據庫,防止流量洪峰導致服務雪崩。雖然原文僅提供標題,但結合生產實踐,梳理出四大核心策略與實現方案。
| 策略 | 原理 | 適用場景 | 實現複雜度 |
|---|---|---|---|
| 令牌桶 | 固定速率生成令牌,請求消耗令牌,桶滿拒絕/等待 | 平滑流量、允許突發、API 限流 | 中 (Guava RateLimiter / Resilience4j) |
| 漏桶 | 請求入隊列,固定速率處理,隊列滿拒絕 | 強制平滑、流量整形 | 中 |
| 滑動窗口計數器 | 時間窗口內統計請求數,超閾值拒絕 | 精確限流、防刷接口 | 低 (Redis Lua 腳本 / Bucket4j) |
| 信號量隔離 | 限制並發線程數,超出拒絕/排隊 | 資源池隔離 (DB連接/線程池) | 低 (Hystrix/Resilience4j Bulkhead) |
@WebFilter("/*")
public class RateLimitFilter implements Filter {
private final RateLimiter limiter = RateLimiter.create(1000); // 每秒 1000 條
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) res;
if (limiter.tryAcquire(1, 100, TimeUnit.MILLISECONDS)) {
chain.doFilter(req, res); // 獲取令牌成功,放行
} else {
resp.setStatus(429); // Too Many Requests
resp.getWriter().write("{\"code\":429,\"msg\":\"請求過於頻繁,請稍後重試\"}");
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
int qps() default 100;
String key() default ""; // 支持 SpEL: "#userId"
}
@Aspect
@Component
public class RateLimitAspect {
private final Map limiters = new ConcurrentHashMap<>();
@Around("@annotation(rateLimit)")
public Object limit(ProceedingJoinPoint pjp, RateLimit rateLimit) throws Throwable {
String key = rateLimit.key().isEmpty() ? pjp.getSignature().toString() : rateLimit.key();
RateLimiter limiter = limiters.computeIfAbsent(key, k -> RateLimiter.create(rateLimit.qps()));
if (limiter.tryAcquire(50, TimeUnit.MILLISECONDS)) {
return pjp.proceed();
}
throw new BusinessException("TOO_MANY_REQUESTS", "操作過於頻繁");
}
}
// Controller 使用
@RateLimit(qps = 50, key = "#userId")
@PostMapping("/order")
public Result createOrder(@RequestBody OrderDTO dto, @CurrentUser Long userId) { ... }
rate_limit_rejected_total、db_connection_pool_usage。請求頻率控制不是單點技術,而是分層防護體系的關鍵一環。建議從「網關全局限流 → 應用業務限流 → 數據庫連接池隔離 → 緩存策略優化」四層落地,構建高可用 Servlet 服務。