瞧瞧别人家的限流,那叫一个优雅!

1 常用限流方案

1.1 固定窗口计数器

核心原理:以固定时间窗口(如1秒)为周期,统计周期内请求数,超过阈值则拒绝后续请求。

具体代码实现如下:

// 线程安全实现(AtomicLong优化版)
public class FixedWindowCounter {
    private final AtomicLong counter = new AtomicLong(0);
    private volatile long windowStart = System.currentTimeMillis();
    private final int maxRequests;
    private final long windowMillis;

    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        if (now - windowStart > windowMillis) {
            if (counter.compareAndSet(counter.get(), 0)) {
                windowStart = now;
            }
        }
        return counter.incrementAndGet() <= maxRequests;
    }
}

致命缺陷:假设设置1秒100次限制,0.9秒时突发100次请求,下一秒0.1秒又放行100次,实际两秒内通过200次。

就像红绿灯切换时车辆抢行,容易引发"临界点突刺"。

适用场景:日志采集、非关键性接口的粗粒度限流

1.2 滑动窗口

核心原理:将时间窗口细分为更小的时间片(如10秒),统计最近N个时间片的请求总和。

基于Redis的Lua脚本如下:

// Redis Lua实现滑动窗口(精确到毫秒)
String lua = """
    local now = tonumber(ARGV
    local window = tonumber(ARGV
    local key = KEYS[1]
    
    redis.call('ZREMRANGEBYSCORE', key, '-inf', now - window)
    local count = redis.call('ZCARD', key)
    
    if count < tonumber(ARGV then
        redis.call('ZADD', key, now, now)
        redis.call('EXPIRE', key, window/1000)
        return 1
    end
    return 0
    """;

技术亮点:某证券交易系统采用滑动窗口后,将API异常率从5%压降至0.3%。

通过Redis ZSET实现时间切片,误差控制在±10ms内。

优势对比

2.3 漏桶算法

核心原理:请求像水流一样进入漏桶,系统以固定速率处理请求。

桶满时新请求被丢弃。

具体实现如下:

// 漏桶动态实现(Semaphore优化版)
public class LeakyBucket {
    private final Semaphore permits;
    private final ScheduledExecutorService scheduler;

    public LeakyBucket(int rate) {
        this.permits = new Semaphore(rate);
        this.scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(() -> permits.release(rate), 1, 1, TimeUnit.SECONDS);
    }

    public boolean tryAcquire() {
        return permits.tryAcquire();
    }
}

技术痛点:某智能家居平台用此方案,确保即使10万台设备同时上报数据,系统仍按500条/秒的速率稳定处理。

但突发流量会导致队列积压,就像用漏斗倒奶茶——珍珠容易卡住。

适用场景:IoT设备控制指令下发、支付渠道限额等需要严格恒定速率的场景

1.4 令牌桶算法

核心原理:以固定速率生成令牌,请求需获取令牌才能执行。

突发流量可消耗桶内积攒的令牌。

具体实现如下:

// Guava RateLimiter高级用法
RateLimiter limiter = RateLimiter.create(10.0, 1, TimeUnit.SECONDS); // 初始预热
limiter.acquire(5); // 尝试获取5个令牌

// 动态调整速率(需反射实现)
Field field = RateLimiter.class.getDeclaredField("tokens");
field.setAccessible(true);
AtomicDouble tokens = (AtomicDouble) field.get(limiter);
tokens.set(20); // 突发时注入20个令牌

实战案例:某视频平台用此方案应对热点事件:平时限制10万QPS,突发时允许3秒内超限50%,既防雪崩又保用户体验。

动态特性

  • 正常时限制QPS

  • 突发时允许透支

  • 持续突发会耗尽令牌

2 生产环境实战

2.1 网关层分布式限流

某电商双11方案:通过Redis+Lua实现分布式计数,配合Nginx本地缓存,在网关层拦截了83%的恶意请求。

2.2 自适应熔断机制

我们还需要自适应熔断机制。

某社交平台用此方案,在突发流量时自动将限流阈值从5万降到3万,等系统恢复后再逐步提升。

3 避坑指南与性能优化

3.1 致命误区

在数据库连接池前做限流!

某公司曾因此导致连接泄漏,最终撑爆数据库。

正确做法应遵循熔断三原则

  1. 快速失败:在入口层拦截无效请求

  2. 动态降级:核心服务保留最小资源

  3. 自动恢复:熔断后渐进式放量

3.2 性能优化

某金融系统通过JMH测试发现,使用LongAdder替代AtomicLong,限流吞吐量提升220%。

性能优化手段:减少CAS竞争 和 分段锁基座。

总结

上面列举了工作中最常用的4种限流方案。

对于不同的业务场景,我们需要选择不同的限流方案。

限流的黄金法则如下:

记住:好的限流方案就像高铁闸机——既保证通行效率,又守住安全底线。

文章转载自:苏三说技术

原文链接:瞧瞧别人家的限流,那叫一个优雅! - 苏三说技术 - 博客园

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值