滑动时间窗口实现重试限流
滑动时间窗口实现重试限流,有效避免在短时间内大量请求失败导致的重试风暴问题。
class SlidingWindow {private List<RequestRecord> window = new ArrayList<>();private int threshold; // 超时阈值private int windowSize; // 窗口大小private long windowDuration; // 窗口时间范围(毫秒)public SlidingWindow(int threshold, int windowSize, long windowDuration) {this.threshold = threshold;this.windowSize = windowSize;this.windowDuration = windowDuration;}synchronized void add(RequestRecord req) {// 移除过期的请求记录long currentTime = System.currentTimeMillis();window.removeIf(r -> (currentTime - r.getTimestamp()) > windowDuration);// 如果窗口已满,移除最早的记录if (window.size() >= windowSize) {window.remove(0);}window.add(req); // 添加请求记录}boolean allowRetry() {int timeoutCount = 0;long currentTime = System.currentTimeMillis();// 统计窗口内超时的失败请求for (RequestRecord r : window) {if (r.isTimeout() && !r.isSuccess() && (currentTime - r.getTimestamp()) <= windowDuration) {timeoutCount++;}}return timeoutCount < threshold; // 判断是否允许重试}
}// 请求记录类
class RequestRecord {private long timestamp; // 请求时间戳private int timeoutThreshold; // 超时阈值(毫秒)private boolean success; // 请求是否成功public RequestRecord(long timestamp, int timeoutThreshold, boolean success) {this.timestamp = timestamp;this.timeoutThreshold = timeoutThreshold;this.success = success;}public boolean isTimeout() {long currentTime = System.currentTimeMillis();return (currentTime - timestamp) > timeoutThreshold; // 判断是否超时的逻辑}public long getTimestamp() {return timestamp;}public boolean isSuccess() {return success;}
}public class OrderController {public static void main(String[] args) {// 初始化滑动窗口,设置超时阈值为3,窗口大小为10,时间窗口为1分钟SlidingWindow slidingWindow = new SlidingWindow(3, 10, 60000);// 模拟请求和重试逻辑for (int i = 0; i < 15; i++) {try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}boolean success = Math.random() > 0.5; // 随机模拟请求成功或失败RequestRecord record = new RequestRecord(System.currentTimeMillis(), 150, success);slidingWindow.add(record);if (!success && slidingWindow.allowRetry()) {System.out.println("请求失败,触发重试");} else if (!success) {System.out.println("请求失败,达到重试阈值,不再重试");} else {System.out.println("请求成功");}}}
}
局限性:
• 内存爆炸:每秒数万次请求需存储数万条记录,内存占用高达40MB(10万QPS场景)。
• 遍历开销:每次判定需遍历全部请求,时间复杂度为O(n),导致CPU峰值负载达70-90%。
• 锁竞争严重:多线程更新窗口需加锁,吞吐量仅5,000 QPS。