【架构师之路】每秒崩溃100次到百万并发:四大限流算法如何拯救你的系统?

前言

随着互联网和移动应用的蓬勃发展,系统的稳定性和可靠性变得至关重要。在面对突发流量和恶意攻击等挑战时,限流器算法的重要性愈发凸显。

限流器算法作为保护系统免受过载的有效手段,扮演着关键的角色。它们能够有效地控制请求的处理速率,防止系统被过多请求淹没,从而保障系统的正常运行并提升用户体验。

在这篇文章中,我们将探讨不同类型的限流器算法,包括漏桶算法、令牌桶算法、固定窗口算法和滑动窗口算法,深入剖析它们的原理、特点以及在实际场景中的应用。通过深入了解和合理应用限流器算法,我们能够更好地应对系统面临的挑战,确保系统的稳定性和可靠性,为用户提供更好的服务体验。

一、基础算法演进路径

1. 固定窗口算法(Fixed Window Algorithm):

固定窗口(又称计数器算法,Fixed Window)限流算法,是最简单的限流算法,通过在单位时间内维护的计数器来控制该时间单位内的最大访问量。

  • 工作原理:固定窗口算法将时间划分为固定大小的窗口,每个窗口内的请求次数不能超过预设的阈值。

在这里插入图片描述

下面是一个简单的固定窗口限流器的示例。

public class FixedWindowRateLimiter {
    private final int limit; // 窗口内允许的最大请求数
    private final long windowSize; // 窗口大小,单位:毫秒
    private AtomicInteger counter = new AtomicInteger(0);
    private long windowStart = System.currentTimeMillis();

    public FixedWindowRateLimiter(int limit, long windowSize) {
        this.limit = limit;
        this.windowSize = windowSize;
    }

    public synchronized boolean allowRequest() {
        long currentTime = System.currentTimeMillis();
        if (currentTime - windowStart > windowSize) {
            // 如果当前时间已经超过了窗口的起始时间,重置计数器和窗口起始时间
            counter.set(0);
            windowStart = currentTime;
        }

        if (counter.get() < limit) {
            counter.incrementAndGet();
            return true;
        } else {
            return false; // 达到限流阈值,拒绝请求
        }
    }
}

核心缺陷

  • 临界突刺:窗口切换时可能承受双倍流量冲击(如第0.9秒和第1.1秒各涌入阈值量请求)
  • 精度不足:1秒窗口无法感知500ms内的突发流量

适用场景

  • 低频接口监控
  • 辅助性流量统计

2. 滑动窗口算法(Sliding Window Algorithm):

为了防止瞬时流量,可以将时间窗口细分为多个子窗口(如10个100ms单元),动态统计最近N个子窗口的请求量。

  • 工作原理:滑动窗口算法通过不断滑动时间窗口,控制每个时间窗口内的请求数量,防止请求过载。
新请求
当前时间戳
清除过期子窗口
创建新子窗口
统计有效窗口计数

在这里插入图片描述

Java示例:滑动窗口算法的实现比较复杂,这里提供一个简单的示例。

import java.util.ArrayDeque;

public class SlidingWindowRateLimiter {
    private final int windowSize; // 窗口大小,单位:毫秒
    private final int limit; // 每个窗口内允许的最大请求数
    private ArrayDeque<Long> timestamps = new ArrayDeque<>();

    public SlidingWindowRateLimiter(int windowSize, int limit) {
        this.windowSize = windowSize;
        this.limit = limit;
    }

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

        // 移除过期的时间戳
        while (!timestamps.isEmpty() && timestamps.peek() <= currentTime - windowSize) {
            timestamps.poll();
        }

        // 判断是否超过限流阈值
        if (timestamps.size() < limit) {
            timestamps.offer(currentTime);
            return true;
        } else {
            return false; // 达到限流阈值,拒绝请求
        }
    }
}

核心优势

  • 解决临界突刺问题,精度提升10倍(100ms粒度)
  • 动态窗口适应流量波动

代价

  • 内存消耗增加(需存储时间片数据)
  • 时钟精度影响准确性

当窗口中流量到达阈值时,流量会瞬间切断,在实际应用中我们要的限流效果往往不是把流量一下子掐断,而是让流量平滑地进入系统当中。


二、流量整形算法

2.1. 漏桶算法(Leaky Bucket Algorithm):

如何更加平滑的限流?不妨看看漏桶算法(Leaky Bucket),请求就像水一样以任意速度注入漏桶,而桶会按照固定的速率将水漏掉;当注入速度持续大于漏出的速度时,漏桶会变满,此时新进入的请求将会被丢弃。限流整形是漏桶算法的两个核心能力。

  • 工作原理:漏桶算法通过固定速率处理请求,多余的请求会被放入一个“桶”中,当桶满时拒绝请求。

在这里插入图片描述

Java示例:漏桶算法的Java实现可以类似于令牌桶算法的示例,这里提供一个简单的框架。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class LeakyBucketRateLimiter {
    private BlockingQueue<Object> bucket;
    private int capacity; // 漏桶容量
    private int leakRate; // 漏桶速率

    public LeakyBucketRateLimiter(int capacity, int leakRate) {
        this.bucket = new ArrayBlockingQueue<>(capacity);
        this.capacity = capacity;
        this.leakRate = leakRate;

        // 开启漏水线程
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000 / leakRate); // 每秒漏水leakRate个请求
                    bucket.poll(); // 漏水
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public boolean allowRequest() {
        if (bucket.size() < capacity) {
            bucket.offer(new Object());
            return true;
        }

        return false; // 桶已满,拒绝请求
    }
}

设计哲学

  • 以恒定速率处理请求(类似物理漏桶),超出队列容量则拒绝

核心价值

  • 绝对速率控制(适合硬件设备限流)
  • 平滑突发流量(输出恒定流量)

局限性

  • 无法应对突发流量高峰
  • 队列延迟影响实时性

不过由于漏桶对流量的控制过于严格,在有些场景下不能充分使用系统资源,因为漏桶的漏出速率是固定的,即使在某一时刻下游能够处理更大的流量,漏桶也不允许突发流量通过。


2.2 令牌桶算法(Token Bucket Algorithm)

  • 工作原理:令牌桶算法通过维护一个固定容量的桶,按照固定速率往桶内放入令牌,请求到来时消耗桶中的令牌,当桶中没有足够的令牌时拒绝请求。

在这里插入图片描述

Java示例:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class TokenBucketRateLimiter {
    private BlockingQueue<Object> tokens;
    private int capacity;
    private int rate;

    public TokenBucketRateLimiter(int capacity, int rate) {
        this.tokens = new ArrayBlockingQueue<>(capacity);
        this.capacity = capacity;
        this.rate = rate;

        // 开启令牌生成线程
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000 / rate); // 每秒生成rate个令牌
                    tokens.offer(new Object()); // 生成令牌
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public boolean allowRequest() {
        return tokens.poll() != null; // 尝试从令牌桶中取出一个令牌,如果成功则允许请求通过
    }

    public static void main(String[] args) {
        TokenBucketRateLimiter rateLimiter = new TokenBucketRateLimiter(10, 2); // 令牌桶容量为10,生成速率为2个/秒

        // 模拟请求
        for (int i = 0; i < 15; i++) {
            if (rateLimiter.allowRequest()) {
                System.out.println("处理请求 " + i);
            } else {
                System.out.println("限流请求 " + i);
            }

            try {
                Thread.sleep(500); // 模拟请求间隔0.5秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

创新设计

  • 定期向桶中添加令牌,请求获取令牌后方可执行

核心优势

  • 允许突发流量(桶内令牌可积累)
  • 动态调整速率(支持预热模式)
  • 兼顾系统保护与资源利用

2.3 算法对比

特性漏桶令牌桶
流量特征平滑输出允许突发
控制维度流出速率流入速率
适用场景保护下游应对突发

三、Java生态实践

3.1 Guava RateLimiter

实现机制

  • 令牌桶变体(支持预热机制)
  • 平滑突发限制(SmoothBursty)
  • 平滑预热(SmoothWarmingUp)

生产配置

RateLimiter limiter = RateLimiter.create(10.0, // 每秒10个令牌
    3, TimeUnit.SECONDS); // 预热时间

3.2 Sentinel

创新设计

滑动窗口统计(秒级200个窗口,5ms精度)

  • 动态规则推送
  • 熔断降级联动
  • 流量控制规则
FlowRule rule = new FlowRule("resA")
    .setGrade(RuleConstant.FLOW_GRADE_QPS)
    .setCount(100) // 阈值
    .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER); // 排队等待

3.3 Resilience4j

组合模式
  • 限流器(RateLimiter)
  • 重试(Retry)
  • 熔断(CircuitBreaker)联动

配置示例

resilience4j.ratelimiter:
  instances:
    backendA:
      limitForPeriod: 50
      limitRefreshPeriod: 1s
      timeoutDuration: 10ms

四、架构选型矩阵

允许
限制
突发流量
令牌桶
漏桶
精准控制
滑动窗口
简单监控
固定窗口

那具体业务场景我们有以下的推荐方案:

场景推荐方案技术组合
API网关限流滑动窗口+令牌桶Nginx + Lua + Redis
微服务接口防护令牌桶Spring Cloud Gateway + Redis
数据库访问控制漏桶算法HikariCP + Semaphore
秒杀系统分层滑动窗口Sentinel + 本地缓存

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值