滑动时间窗口实现重试限流

发布于:2025-04-21 ⋅ 阅读:(21) ⋅ 点赞:(0)

滑动时间窗口实现重试限流,有效避免在短时间内大量请求失败导致的重试风暴问题。

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。


网站公告

今日签到

点亮在社区的每一天
去签到