返回

限流算法的Java实现解析,提升系统稳定性

后端

在高并发场景下,系统可能面临瞬间请求量激增的情况,超过系统所能处理的极限。这时,就需要采用限流算法来保护系统,防止其崩溃。限流算法的作用是限制请求的速率,使其不超过系统的处理能力,从而保证系统的稳定性和性能。

滑动日志算法

滑动日志算法是一种简单的限流算法,它通过维护一个固定大小的日志来记录请求的时间戳。当新的请求到来时,首先检查日志中是否已经存在该请求的时间戳。如果存在,则认为该请求被限流,否则将该请求的时间戳添加到日志中。日志中的时间戳会随着时间的推移而不断过期,因此算法能够动态地适应请求速率的变化。

滑动日志算法的Java实现

import java.util.LinkedList;

public class SlidingLogRateLimiter {
    private final LinkedList<Long> timestamps;
    private final int capacity;

    public SlidingLogRateLimiter(int capacity) {
        this.timestamps = new LinkedList<>();
        this.capacity = capacity;
    }

    public boolean allowRequest() {
        long now = System.currentTimeMillis();

        // 检查日志是否已满
        if (timestamps.size() >= capacity) {
            // 如果已满,则移除最旧的时间戳
            timestamps.removeFirst();
        }

        // 将新的时间戳添加到日志中
        timestamps.addLast(now);

        // 计算当前请求速率
        long windowSize = now - timestamps.getFirst();
        double rate = (double) timestamps.size() / windowSize * 1000;

        // 如果当前请求速率超过限制,则限流
        return rate < capacity;
    }
}

令牌桶算法

令牌桶算法是一种基于令牌的限流算法。它通过维护一个固定大小的令牌桶来控制请求的速率。当新的请求到来时,首先检查令牌桶中是否有足够的令牌。如果有,则消耗一个令牌并允许请求通过。如果没有,则限流。令牌桶中的令牌会随着时间的推移而不断生成,因此算法能够动态地适应请求速率的变化。

令牌桶算法的Java实现

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TokenBucketRateLimiter {
    private final int capacity;
    private final long refillRate;
    private final Lock lock;
    private long lastRefillTime;
    private int tokens;

    public TokenBucketRateLimiter(int capacity, long refillRate) {
        this.capacity = capacity;
        this.refillRate = refillRate;
        this.lock = new ReentrantLock();
        this.lastRefillTime = System.currentTimeMillis();
        this.tokens = capacity;
    }

    public boolean allowRequest() {
        lock.lock();
        try {
            // 计算令牌桶中的当前令牌数
            long now = System.currentTimeMillis();
            long elapsedTime = now - lastRefillTime;
            tokens = Math.min(capacity, tokens + (int) (elapsedTime * refillRate / 1000));

            // 如果有足够的令牌,则消耗一个令牌并允许请求通过
            if (tokens > 0) {
                tokens--;
                lastRefillTime = now;
                return true;
            } else {
                // 否则,限流
                return false;
            }
        } finally {
            lock.unlock();
        }
    }
}

漏桶算法

漏桶算法是一种基于队列的限流算法。它通过维护一个固定大小的队列来控制请求的速率。当新的请求到来时,首先检查队列是否已满。如果已满,则限流。如果没有,则将请求添加到队列中。队列中的请求会随着时间的推移而不断出队,因此算法能够动态地适应请求速率的变化。

漏桶算法的Java实现

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class LeakyBucketRateLimiter {
    private final BlockingQueue<Long> queue;
    private final long capacity;
    private final long refillRate;

    public LeakyBucketRateLimiter(long capacity, long refillRate) {
        this.queue = new LinkedBlockingQueue<>((int) capacity);
        this.capacity = capacity;
        this.refillRate = refillRate;
    }

    public boolean allowRequest() {
        // 尝试将请求添加到队列中
        boolean success = queue.offer(System.currentTimeMillis());

        // 如果队列已满,则限流
        if (!success) {
            return false;
        }

        // 如果队列未满,则移除队列中的最旧请求
        long now = System.currentTimeMillis();
        while (!queue.isEmpty() && now - queue.peek() > capacity) {
            queue.poll();
        }

        return true;
    }
}

结语

本文详细介绍了滑动日志、令牌桶和漏桶三种常见的限流算法及其Java实现,并分析了其优缺点。希望通过本文,您能够轻松掌握限流算法并在实际项目中应用,有效提升系统稳定性和性能。