架构之限流

架构之限流

引言

在现代分布式系统中,流量控制是保障系统稳定性的核心机制之一。当系统面临突发流量时,如果没有有效的限流策略,可能导致服务雪崩、资源耗尽、下游系统过载等严重后果。

限流法则强调:高可用系统必须具备流量控制能力,通过计数器、漏桶、令牌桶等限流算法,在系统容量边界内平滑处理请求,保护自身系统和下游系统不被巨型流量冲垮,确保核心服务的可用性

限流架构的核心理念

为什么需要限流?

流量控制挑战

突发流量冲击

系统容量限制

下游保护需求

资源合理分配

用户体验保障

热点事件流量激增

恶意攻击流量

用户行为聚集

服务器CPU/内存有限

数据库连接数限制

网络带宽瓶颈

第三方API调用限制

下游服务保护

依赖系统稳定性

核心业务优先

资源公平分配

成本控制需求

避免服务不可用

提供降级服务

响应时间可控

微博案例:明星恋情公布

场景描述

某明星在微博公布恋情,引发了巨大的流量冲击:

50万QPS

突发流量

超出容量

限流保护

不限流

平时流量

系统正常运行

明星公布恋情

500万QPS

系统规划容量200万QPS

服务可用状态

系统崩溃

流量分析

指标平时状态突发状态系统容量
访问量50万QPS500万QPS200万QPS
流量倍数1x10x4x
处理策略正常处理需要限流限流阈值

限流策略

  1. 核心接口限流:保证核心功能可用
  2. 非核心接口降级:降低优先级接口的限流阈值
  3. 用户维度限流:限制单个用户的请求频率
  4. 地域维度限流:根据不同地区的流量情况调整限流策略

限流算法体系

限流算法

计数器算法

漏桶算法

令牌桶算法

固定窗口计数器

滑动窗口计数器

恒定速率输出

平滑流量

削峰填谷

令牌生成

令牌获取

突发流量处理

简单易实现

边界问题

流量平滑

无法处理突发

灵活配置

支持突发

计数器算法

固定窗口计数器

核心原理

固定窗口计数器是最简单的限流算法,将时间划分为固定大小的窗口,在每个窗口内统计请求次数,超过阈值则拒绝请求。

系统计数器客户端系统计数器客户端时间窗口 1 (0-1000ms)count < limit, 允许时间窗口 2 (1000-2000ms)count >= limit, 拒绝请求1count = 1请求2count = 2请求Ncount = N请求1count = 1 (重置)请求N+1count = N+1
代码实现
/**
 * 固定窗口计数器限流器
 */
@Component
public class FixedWindowRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(FixedWindowRateLimiter.class);
    
    private final long windowSizeInMillis;  // 窗口大小(毫秒)
    private final int maxRequests;           // 最大请求数
    private final AtomicLong counter;        // 请求计数器
    private final AtomicLong windowStart;    // 窗口开始时间
    
    /**
     * 构造函数
     * @param windowSizeInSeconds 窗口大小(秒)
     * @param maxRequests 最大请求数
     */
    public FixedWindowRateLimiter(int windowSizeInSeconds, int maxRequests) {
        this.windowSizeInMillis = windowSizeInSeconds * 1000L;
        this.maxRequests = maxRequests;
        this.counter = new AtomicLong(0);
        this.windowStart = new AtomicLong(System.currentTimeMillis());
        
        log.info("初始化固定窗口限流器: 窗口大小={}秒, 最大请求={}", 
            windowSizeInSeconds, maxRequests);
    }
    
    /**
     * 尝试获取访问许可
     * @return true表示允许访问,false表示被限流
     */
    public boolean tryAcquire() {
        long currentTime = System.currentTimeMillis();
        long currentWindowStart = windowStart.get();
        
        // 检查是否需要重置窗口
        if (currentTime - currentWindowStart >= windowSizeInMillis) {
            // 使用CAS操作重置窗口
            if (windowStart.compareAndSet(currentWindowStart, currentTime)) {
                counter.set(0);
                log.debug("重置计数器窗口: {}", currentTime);
            }
        }
        
        // 增加计数
        long currentCount = counter.incrementAndGet();
        
        if (currentCount <= maxRequests) {
            log.debug("请求允许: 当前计数={}, 最大请求={}", currentCount, maxRequests);
            return true;
        } else {
            // 超过限制,回滚计数
            counter.decrementAndGet();
            log.warn("请求被限流: 当前计数={}, 最大请求={}", currentCount, maxRequests);
            return false;
        }
    }
    
    /**
     * 获取当前窗口的请求计数
     */
    public long getCurrentCount() {
        return counter.get();
    }
    
    /**
     * 获取剩余可用请求数
     */
    public int getAvailablePermits() {
        return Math.max(0, maxRequests - (int) counter.get());
    }
    
    /**
     * 性能测试
     */
    public void performanceTest() {
        log.info("=== 固定窗口计数器性能测试 ===");
        
        FixedWindowRateLimiter limiter = new FixedWindowRateLimiter(1, 1000);
        int totalRequests = 10000;
        int allowedCount = 0;
        int rejectedCount = 0;
        
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < totalRequests; i++) {
            if (limiter.tryAcquire()) {
                allowedCount++;
            } else {
                rejectedCount++;
            }
            
            // 模拟请求间隔
            try {
                Thread.sleep(0, 100000); // 0.1ms
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        long endTime = System.currentTimeMillis();
        
        log.info("总请求数: {}, 允许: {}, 拒绝: {}, 耗时: {}ms", 
            totalRequests, allowedCount, rejectedCount, endTime - startTime);
    }
}

/**
 * 固定窗口计数器限流器(使用ConcurrentHashMap支持多维度限流)
 */
@Component
public class MultiKeyFixedWindowRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(MultiKeyFixedWindowRateLimiter.class);
    
    private final long windowSizeInMillis;
    private final int maxRequests;
    private final ConcurrentHashMap<String, WindowCounter> counters;
    
    /**
     * 窗口计数器内部类
     */
    private static class WindowCounter {
        private final AtomicLong count;
        private final AtomicLong windowStart;
        
        public WindowCounter(long windowStart) {
            this.count = new AtomicLong(0);
            this.windowStart = new AtomicLong(windowStart);
        }
    }
    
    public MultiKeyFixedWindowRateLimiter(int windowSizeInSeconds, int maxRequests) {
        this.windowSizeInMillis = windowSizeInSeconds * 1000L;
        this.maxRequests = maxRequests;
        this.counters = new ConcurrentHashMap<>();
        
        log.info("初始化多维度固定窗口限流器: 窗口大小={}秒, 最大请求={}", 
            windowSizeInSeconds, maxRequests);
    }
    
    /**
     * 尝试获取访问许可
     * @param key 限流维度(如用户ID、IP地址等)
     * @return true表示允许访问,false表示被限流
     */
    public boolean tryAcquire(String key) {
        long currentTime = System.currentTimeMillis();
        
        // 获取或创建计数器
        WindowCounter counter = counters.computeIfAbsent(key, 
            k -> new WindowCounter(currentTime));
        
        long currentWindowStart = counter.windowStart.get();
        
        // 检查是否需要重置窗口
        if (currentTime - currentWindowStart >= windowSizeInMillis) {
            if (counter.windowStart.compareAndSet(currentWindowStart, currentTime)) {
                counter.count.set(0);
                log.debug("重置计数器窗口: key={}, 时间={}", key, currentTime);
            }
        }
        
        // 增加计数
        long currentCount = counter.count.incrementAndGet();
        
        if (currentCount <= maxRequests) {
            return true;
        } else {
            counter.count.decrementAndGet();
            log.warn("请求被限流: key={}, 当前计数={}", key, currentCount);
            return false;
        }
    }
    
    /**
     * 清理过期的计数器
     */
    public void cleanupExpiredCounters() {
        long currentTime = System.currentTimeMillis();
        long expireTime = currentTime - windowSizeInMillis * 2;
        
        counters.entrySet().removeIf(entry -> {
            WindowCounter counter = entry.getValue();
            return counter.windowStart.get() < expireTime;
        });
        
        log.debug("清理过期计数器完成, 当前计数器数量: {}", counters.size());
    }
}
边界问题分析

固定窗口计数器存在临界突变问题:

时间轴

窗口1: 0-1000ms

窗口2: 1000-2000ms

前900ms: 100请求

后100ms: 900请求

总计: 1000请求

前100ms: 900请求

后900ms: 100请求

总计: 1000请求

临界点: 100ms内1800请求

问题: 限流阈值1000, 实际通过1800

问题说明

  • 假设限流阈值为1000请求/秒
  • 窗口1的最后100ms来了900个请求
  • 窗口2的前100ms来了900个请求
  • 在200ms内实际通过了1800个请求,远超限流阈值

滑动窗口计数器

核心原理

滑动窗口计数器通过将时间窗口划分为更细的粒度,解决了固定窗口的临界突变问题。

滑动窗口

细粒度子窗口

滑动计算

平滑限流

窗口大小: 1秒

子窗口: 100ms

子窗口数量: 10个

当前时间: 850ms

滑动窗口: 850ms-1850ms

统计范围: 子窗口6-15

精确控制

无临界突变

资源消耗较大

代码实现
/**
 * 滑动窗口计数器限流器
 */
@Component
public class SlidingWindowRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(SlidingWindowRateLimiter.class);
    
    private final long windowSizeInMillis;      // 窗口总大小
    private final int subWindowCount;           // 子窗口数量
    private final long subWindowSizeInMillis;   // 子窗口大小
    private final int maxRequests;              // 最大请求数
    
    private final AtomicReferenceArray<SubWindow> subWindows;
    private final AtomicInteger currentIndex;
    
    /**
     * 子窗口内部类
     */
    private static class SubWindow {
        private final AtomicLong count;
        private final AtomicLong timestamp;
        
        public SubWindow(long timestamp) {
            this.count = new AtomicLong(0);
            this.timestamp = new AtomicLong(timestamp);
        }
        
        public void reset(long newTimestamp) {
            count.set(0);
            timestamp.set(newTimestamp);
        }
    }
    
    /**
     * 构造函数
     * @param windowSizeInSeconds 窗口大小(秒)
     * @param subWindowCount 子窗口数量
     * @param maxRequests 最大请求数
     */
    public SlidingWindowRateLimiter(int windowSizeInSeconds, int subWindowCount, int maxRequests) {
        this.windowSizeInMillis = windowSizeInSeconds * 1000L;
        this.subWindowCount = subWindowCount;
        this.subWindowSizeInMillis = windowSizeInMillis / subWindowCount;
        this.maxRequests = maxRequests;
        
        // 初始化子窗口
        this.subWindows = new AtomicReferenceArray<>(subWindowCount);
        long currentTime = System.currentTimeMillis();
        for (int i = 0; i < subWindowCount; i++) {
            subWindows.set(i, new SubWindow(currentTime - i * subWindowSizeInMillis));
        }
        
        this.currentIndex = new AtomicInteger(0);
        
        log.info("初始化滑动窗口限流器: 窗口大小={}秒, 子窗口数量={}, 最大请求={}", 
            windowSizeInSeconds, subWindowCount, maxRequests);
    }
    
    /**
     * 尝试获取访问许可
     */
    public boolean tryAcquire() {
        long currentTime = System.currentTimeMillis();
        
        // 计算当前子窗口索引
        int index = (int) ((currentTime / subWindowSizeInMillis) % subWindowCount);
        int oldIndex = currentIndex.get();
        
        // 检查是否需要切换到新的子窗口
        if (index != oldIndex) {
            if (currentIndex.compareAndSet(oldIndex, index)) {
                SubWindow window = subWindows.get(index);
                long windowTimestamp = window.timestamp.get();
                
                // 检查子窗口是否过期
                if (currentTime - windowTimestamp >= windowSizeInMillis) {
                    window.reset(currentTime);
                }
            }
        }
        
        // 增加当前子窗口的计数
        SubWindow currentWindow = subWindows.get(index);
        long currentCount = currentWindow.count.incrementAndGet();
        
        // 计算滑动窗口内的总请求数
        long totalCount = calculateTotalCount(currentTime);
        
        if (totalCount <= maxRequests) {
            log.debug("请求允许: 当前计数={}, 滑动窗口计数={}", currentCount, totalCount);
            return true;
        } else {
            // 超过限制,回滚计数
            currentWindow.count.decrementAndGet();
            log.warn("请求被限流: 滑动窗口计数={}, 最大请求={}", totalCount, maxRequests);
            return false;
        }
    }
    
    /**
     * 计算滑动窗口内的总请求数
     */
    private long calculateTotalCount(long currentTime) {
        long totalCount = 0;
        long windowStart = currentTime - windowSizeInMillis;
        
        for (int i = 0; i < subWindowCount; i++) {
            SubWindow window = subWindows.get(i);
            long windowTimestamp = window.timestamp.get();
            
            // 只统计滑动窗口内的子窗口
            if (windowTimestamp > windowStart) {
                totalCount += window.count.get();
            }
        }
        
        return totalCount;
    }
    
    /**
     * 获取当前窗口的请求计数
     */
    public long getCurrentCount() {
        return calculateTotalCount(System.currentTimeMillis());
    }
    
    /**
     * 获取剩余可用请求数
     */
    public int getAvailablePermits() {
        return Math.max(0, maxRequests - (int) getCurrentCount());
    }
    
    /**
     * 性能测试
     */
    public void performanceTest() {
        log.info("=== 滑动窗口计数器性能测试 ===");
        
        SlidingWindowRateLimiter limiter = new SlidingWindowRateLimiter(1, 10, 1000);
        int totalRequests = 10000;
        int allowedCount = 0;
        int rejectedCount = 0;
        
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < totalRequests; i++) {
            if (limiter.tryAcquire()) {
                allowedCount++;
            } else {
                rejectedCount++;
            }
            
            // 模拟请求间隔
            try {
                Thread.sleep(0, 100000); // 0.1ms
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        long endTime = System.currentTimeMillis();
        
        log.info("总请求数: {}, 允许: {}, 拒绝: {}, 耗时: {}ms", 
            totalRequests, allowedCount, rejectedCount, endTime - startTime);
    }
}

计数器算法适用场景

场景1:API网关限流

应用场景

  • 保护后端服务不被过载
  • 防止恶意攻击
  • 公平分配资源

实现特点

  • 使用滑动窗口算法
  • 支持多维度限流(IP、用户、API)
  • 可配置不同接口的限流阈值
/**
 * API网关限流器
 */
@Component
public class ApiGatewayRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(ApiGatewayRateLimiter.class);
    
    private final Map<String, SlidingWindowRateLimiter> apiLimiters;
    private final SlidingWindowRateLimiter globalLimiter;
    
    public ApiGatewayRateLimiter() {
        this.apiLimiters = new ConcurrentHashMap<>();
        
        // 全局限流:10000 QPS
        this.globalLimiter = new SlidingWindowRateLimiter(1, 10, 10000);
        
        // 各API限流配置
        apiLimiters.put("/api/user/login", new SlidingWindowRateLimiter(1, 10, 1000));
        apiLimiters.put("/api/order/create", new SlidingWindowRateLimiter(1, 10, 500));
        apiLimiters.put("/api/product/list", new SlidingWindowRateLimiter(1, 10, 2000));
        
        log.info("API网关限流器初始化完成");
    }
    
    /**
     * 检查请求是否允许通过
     */
    public boolean allowRequest(String apiPath, String userId) {
        // 1. 检查全局限流
        if (!globalLimiter.tryAcquire()) {
            log.warn("全局限流触发: api={}", apiPath);
            return false;
        }
        
        // 2. 检查API级别限流
        SlidingWindowRateLimiter apiLimiter = apiLimiters.get(apiPath);
        if (apiLimiter != null && !apiLimiter.tryAcquire()) {
            log.warn("API限流触发: api={}", apiPath);
            return false;
        }
        
        // 3. 检查用户级别限流
        String userKey = "user:" + userId;
        // 这里可以添加用户级别限流逻辑
        
        return true;
    }
}
场景2:用户行为限流

应用场景

  • 防止用户恶意刷接口
  • 保护系统资源
  • 提供公平的服务

实现特点

  • 基于用户ID限流
  • 不同用户不同限流策略
  • VIP用户更高限流阈值
/**
 * 用户行为限流器
 */
@Component
public class UserBehaviorRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(UserBehaviorRateLimiter.class);
    
    private final ConcurrentHashMap<String, SlidingWindowRateLimiter> userLimiters;
    private final SlidingWindowRateLimiter defaultLimiter;
    private final SlidingWindowRateLimiter vipLimiter;
    
    public UserBehaviorRateLimiter() {
        this.userLimiters = new ConcurrentHashMap<>();
        
        // 普通用户:100 QPS
        this.defaultLimiter = new SlidingWindowRateLimiter(1, 10, 100);
        
        // VIP用户:500 QPS
        this.vipLimiter = new SlidingWindowRateLimiter(1, 10, 500);
        
        log.info("用户行为限流器初始化完成");
    }
    
    /**
     * 检查用户请求是否允许
     */
    public boolean allowUserRequest(String userId, boolean isVip) {
        SlidingWindowRateLimiter limiter;
        
        if (isVip) {
            limiter = vipLimiter;
        } else {
            limiter = defaultLimiter;
        }
        
        boolean allowed = limiter.tryAcquire();
        
        if (!allowed) {
            log.warn("用户限流触发: userId={}, isVip={}", userId, isVip);
        }
        
        return allowed;
    }
}

计数器算法性能对比

算法类型精确度实现复杂度内存占用适用场景
固定窗口低(临界突变)简单对精确度要求不高的场景
滑动窗口中等中等需要精确限流的场景
多维度限流较高较高需要多维度控制的场景

漏桶算法

核心原理

漏桶算法将请求看作水滴,漏桶看作一个固定容量的桶,水滴以任意速率流入桶中,桶以恒定速率向外滴水。如果桶满了,多余的水滴会溢出(被拒绝)。

请求流入

漏桶

桶是否已满

请求进入桶

请求被拒绝

队列等待

恒定速率处理

请求流出

漏桶特性

漏桶特性

平滑流量

削峰填谷

恒定输出

无法处理突发

输出速率恒定

避免流量突变

吸收突发流量

平滑处理请求

固定处理速率

保护下游系统

突发流量被丢弃

不适合突发场景

代码实现

/**
 * 漏桶限流器
 */
@Component
public class LeakyBucketRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(LeakyBucketRateLimiter.class);
    
    private final long capacity;                // 桶容量
    private final long leakRateInMillis;        // 漏水速率(每个请求间隔毫秒数)
    private final BlockingQueue<Request> bucket;  // 桶(队列)
    
    private final AtomicLong lastLeakTime;      // 上次漏水时间
    private final ScheduledExecutorService leakExecutor;  // 漏水定时器
    
    /**
     * 请求内部类
     */
    private static class Request {
        private final long timestamp;
        private final CompletableFuture<Boolean> future;
        
        public Request() {
            this.timestamp = System.currentTimeMillis();
            this.future = new CompletableFuture<>();
        }
    }
    
    /**
     * 构造函数
     * @param capacity 桶容量
     * @param leakRatePerSecond 漏水速率(每秒请求数)
     */
    public LeakyBucketRateLimiter(int capacity, int leakRatePerSecond) {
        this.capacity = capacity;
        this.leakRateInMillis = 1000L / leakRatePerSecond;
        this.bucket = new LinkedBlockingQueue<>(capacity);
        this.lastLeakTime = new AtomicLong(System.currentTimeMillis());
        
        // 启动漏水定时器
        this.leakExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread thread = new Thread(r, "leaky-bucket-leak-thread");
            thread.setDaemon(true);
            return thread;
        });
        
        // 启动漏水任务
        this.leakExecutor.scheduleAtFixedRate(
            this::leak,
            leakRateInMillis,
            leakRateInMillis,
            TimeUnit.MILLISECONDS
        );
        
        log.info("初始化漏桶限流器: 容量={}, 漏水速率={}/秒", capacity, leakRatePerSecond);
    }
    
    /**
     * 漏水操作
     */
    private void leak() {
        Request request = bucket.poll();
        if (request != null) {
            // 请求通过
            request.future.complete(true);
            log.debug("请求通过: 等待时间={}ms", 
                System.currentTimeMillis() - request.timestamp);
        }
    }
    
    /**
     * 尝试获取访问许可(异步)
     */
    public CompletableFuture<Boolean> tryAcquireAsync() {
        Request request = new Request();
        
        // 尝试将请求放入桶中
        if (bucket.offer(request)) {
            return request.future;
        } else {
            // 桶已满,请求被拒绝
            log.warn("请求被拒绝: 桶已满");
            return CompletableFuture.completedFuture(false);
        }
    }
    
    /**
     * 尝试获取访问许可(同步)
     */
    public boolean tryAcquire() {
        Request request = new Request();
        
        // 尝试将请求放入桶中
        if (bucket.offer(request)) {
            try {
                // 等待请求处理完成
                return request.future.get(5, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("等待请求处理异常", e);
                return false;
            }
        } else {
            // 桶已满,请求被拒绝
            log.warn("请求被拒绝: 桶已满");
            return false;
        }
    }
    
    /**
     * 获取当前桶中的请求数量
     */
    public int getCurrentSize() {
        return bucket.size();
    }
    
    /**
     * 获取剩余容量
     */
    public int getRemainingCapacity() {
        return bucket.remainingCapacity();
    }
    
    /**
     * 关闭限流器
     */
    public void shutdown() {
        leakExecutor.shutdown();
        try {
            if (!leakExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                leakExecutor.shutdownNow();
            }
        } catch (InterruptedException e) {
            leakExecutor.shutdownNow();
            Thread.currentThread().interrupt();
        }
        
        log.info("漏桶限流器已关闭");
    }
    
    /**
     * 性能测试
     */
    public void performanceTest() {
        log.info("=== 漏桶算法性能测试 ===");
        
        LeakyBucketRateLimiter limiter = new LeakyBucketRateLimiter(100, 100);
        int totalRequests = 1000;
        int allowedCount = 0;
        int rejectedCount = 0;
        
        long startTime = System.currentTimeMillis();
        
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        
        for (int i = 0; i < totalRequests; i++) {
            final int requestNum = i;
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                boolean allowed = limiter.tryAcquire();
                synchronized (LeakyBucketRateLimiter.this) {
                    if (allowed) {
                        allowedCount++;
                    } else {
                        rejectedCount++;
                    }
                }
            });
            futures.add(future);
        }
        
        // 等待所有请求完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        
        long endTime = System.currentTimeMillis();
        
        log.info("总请求数: {}, 允许: {}, 拒绝: {}, 耗时: {}ms", 
            totalRequests, allowedCount, rejectedCount, endTime - startTime);
        
        limiter.shutdown();
    }
}

/**
 * 分布式漏桶限流器(基于Redis)
 */
@Component
public class DistributedLeakyBucketRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(DistributedLeakyBucketRateLimiter.class);
    
    private final StringRedisTemplate redisTemplate;
    private final long capacity;
    private final long leakRateInMillis;
    
    private static final String BUCKET_PREFIX = "leaky:bucket:";
    private static final String BUCKET_SIZE_SUFFIX = ":size";
    private static final String BUCKET_LAST_LEAK_SUFFIX = ":last_leak";
    
    public DistributedLeakyBucketRateLimiter(StringRedisTemplate redisTemplate, 
            int capacity, int leakRatePerSecond) {
        this.redisTemplate = redisTemplate;
        this.capacity = capacity;
        this.leakRateInMillis = 1000L / leakRatePerSecond;
        
        log.info("初始化分布式漏桶限流器: 容量={}, 漏水速率={}/秒", capacity, leakRatePerSecond);
    }
    
    /**
     * 尝试获取访问许可
     * @param key 限流键
     * @return true表示允许,false表示被拒绝
     */
    public boolean tryAcquire(String key) {
        String sizeKey = BUCKET_PREFIX + key + BUCKET_SIZE_SUFFIX;
        String lastLeakKey = BUCKET_PREFIX + key + BUCKET_LAST_LEAK_SUFFIX;
        long currentTime = System.currentTimeMillis();
        
        // 使用Lua脚本保证原子性
        String luaScript = 
            "local sizeKey = KEYS[1]\n" +
            "local lastLeakKey = KEYS[2]\n" +
            "local capacity = tonumber(ARGV[1])\n" +
            "local leakRate = tonumber(ARGV[2])\n" +
            "local currentTime = tonumber(ARGV[3])\n" +
            "\n" +
            "local size = tonumber(redis.call('GET', sizeKey) or 0)\n" +
            "local lastLeakTime = tonumber(redis.call('GET', lastLeakKey) or currentTime)\n" +
            "\n" +
            "-- 计算应该漏掉的数量\n" +
            "local elapsedTime = currentTime - lastLeakTime\n" +
            "local leaked = math.floor(elapsedTime / leakRate)\n" +
            "\n" +
            "-- 更新桶大小\n" +
            "size = math.max(0, size - leaked)\n" +
            "redis.call('SET', lastLeakKey, currentTime)\n" +
            "\n" +
            "-- 检查是否可以添加请求\n" +
            "if size < capacity then\n" +
            "    size = size + 1\n" +
            "    redis.call('SET', sizeKey, size)\n" +
            "    return 1\n" +
            "else\n" +
            "    return 0\n" +
            "end";
        
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        Long result = redisTemplate.execute(redisScript, 
            Arrays.asList(sizeKey, lastLeakKey), 
            capacity, leakRateInMillis, currentTime);
        
        boolean allowed = result != null && result == 1;
        
        if (!allowed) {
            log.warn("分布式漏桶限流: key={}", key);
        }
        
        return allowed;
    }
    
    /**
     * 获取当前桶大小
     */
    public long getCurrentSize(String key) {
        String sizeKey = BUCKET_PREFIX + key + BUCKET_SIZE_SUFFIX;
        String sizeStr = redisTemplate.opsForValue().get(sizeKey);
        return sizeStr != null ? Long.parseLong(sizeStr) : 0;
    }
}

漏桶算法适用场景

场景1:数据库连接池限流

应用场景

  • 保护数据库不被过载
  • 平滑数据库访问流量
  • 控制并发连接数
/**
 * 数据库连接池限流器
 */
@Component
public class DatabaseConnectionRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(DatabaseConnectionRateLimiter.class);
    
    private final LeakyBucketRateLimiter connectionLimiter;
    
    public DatabaseConnectionRateLimiter() {
        // 桶容量:最大连接数
        // 漏水速率:每秒处理的最大连接数
        this.connectionLimiter = new LeakyBucketRateLimiter(100, 50);
        
        log.info("数据库连接池限流器初始化完成");
    }
    
    /**
     * 获取数据库连接
     */
    public Connection getConnection() throws SQLException {
        // 等待获取连接许可
        if (connectionLimiter.tryAcquire()) {
            // 获取实际连接
            return getActualConnection();
        } else {
            throw new SQLException("连接池已满,请稍后重试");
        }
    }
    
    private Connection getActualConnection() {
        // 实际获取数据库连接的逻辑
        return null;
    }
}
场景2:消息队列消费者限流

应用场景

  • 控制消息消费速率
  • 保护下游处理系统
  • 平滑消息处理流量
/**
 * 消息队列消费者限流器
 */
@Component
public class MessageConsumerRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(MessageConsumerRateLimiter.class);
    
    private final LeakyBucketRateLimiter consumerLimiter;
    
    public MessageConsumerRateLimiter() {
        // 桶容量:缓冲队列大小
        // 漏水速率:每秒处理的消息数
        this.consumerLimiter = new LeakyBucketRateLimiter(1000, 100);
        
        log.info("消息队列消费者限流器初始化完成");
    }
    
    /**
     * 处理消息
     */
    public void processMessage(Message message) {
        // 异步处理消息
        CompletableFuture.runAsync(() -> {
            try {
                // 等待处理许可
                if (consumerLimiter.tryAcquire()) {
                    handleMessage(message);
                } else {
                    log.warn("消息处理限流: messageId={}", message.getId());
                    // 可以将消息重新放入队列或发送到死信队列
                }
            } catch (Exception e) {
                log.error("处理消息异常", e);
            }
        });
    }
    
    private void handleMessage(Message message) {
        // 实际处理消息的逻辑
    }
}

漏桶算法优缺点

优点缺点
流量平滑,输出速率恒定无法处理突发流量
削峰填谷,保护下游系统突发流量会被丢弃
实现相对简单需要维护队列,占用内存
适合保护有处理能力限制的系统不适合需要快速响应的场景

令牌桶算法

核心原理

令牌桶算法以恒定速率向桶中放入令牌,请求到达时从桶中获取令牌,如果桶中有令牌则允许请求通过,否则拒绝请求。令牌桶可以积累一定数量的令牌,因此可以处理突发流量。

令牌生成器

令牌桶

桶中有令牌

获取令牌

请求被拒绝

请求通过

令牌桶特性

令牌桶特性

恒定生成令牌

支持突发流量

灵活配置

平滑限流

固定速率生成

令牌积累

桶满可积累

突发请求可处理

可配置生成速率

可配置桶容量

长期平均速率恒定

短期可突发

代码实现

/**
 * 令牌桶限流器
 */
@Component
public class TokenBucketRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(TokenBucketRateLimiter.class);
    
    private final long capacity;                // 桶容量
    private final long refillRateInMillis;      // 令牌补充速率(每个令牌间隔毫秒数)
    private final AtomicLong tokens;            // 当前令牌数
    private final AtomicLong lastRefillTime;    // 上次补充令牌时间
    
    /**
     * 构造函数
     * @param capacity 桶容量
     * @param refillRatePerSecond 令牌补充速率(每秒令牌数)
     */
    public TokenBucketRateLimiter(long capacity, long refillRatePerSecond) {
        this.capacity = capacity;
        this.refillRateInMillis = 1000L / refillRatePerSecond;
        this.tokens = new AtomicLong(capacity);
        this.lastRefillTime = new AtomicLong(System.currentTimeMillis());
        
        log.info("初始化令牌桶限流器: 容量={}, 补充速率={}/秒", capacity, refillRatePerSecond);
    }
    
    /**
     * 补充令牌
     */
    private void refillTokens() {
        long currentTime = System.currentTimeMillis();
        long lastRefill = lastRefillTime.get();
        long elapsedTime = currentTime - lastRefill;
        
        if (elapsedTime >= refillRateInMillis) {
            // 计算应该补充的令牌数
            long tokensToAdd = elapsedTime / refillRateInMillis;
            
            // 使用CAS操作更新令牌数
            long currentTokens = tokens.get();
            long newTokens = Math.min(capacity, currentTokens + tokensToAdd);
            
            if (tokens.compareAndSet(currentTokens, newTokens)) {
                lastRefillTime.set(currentTime - (elapsedTime % refillRateInMillis));
                log.debug("补充令牌: 新增={}, 当前={}", tokensToAdd, newTokens);
            }
        }
    }
    
    /**
     * 尝试获取访问许可
     * @return true表示允许,false表示被拒绝
     */
    public boolean tryAcquire() {
        refillTokens();
        
        // 尝试获取令牌
        while (true) {
            long currentTokens = tokens.get();
            if (currentTokens > 0) {
                if (tokens.compareAndSet(currentTokens, currentTokens - 1)) {
                    log.debug("获取令牌成功: 剩余令牌={}", currentTokens - 1);
                    return true;
                }
            } else {
                log.warn("获取令牌失败: 桶已空");
                return false;
            }
        }
    }
    
    /**
     * 尝试获取多个访问许可
     * @param permits 需要的许可数
     * @return true表示允许,false表示被拒绝
     */
    public boolean tryAcquire(int permits) {
        if (permits <= 0) {
            return true;
        }
        
        refillTokens();
        
        // 尝试获取多个令牌
        while (true) {
            long currentTokens = tokens.get();
            if (currentTokens >= permits) {
                if (tokens.compareAndSet(currentTokens, currentTokens - permits)) {
                    log.debug("获取令牌成功: 获取={}, 剩余={}", permits, currentTokens - permits);
                    return true;
                }
            } else {
                log.warn("获取令牌失败: 需要={}, 当前={}", permits, currentTokens);
                return false;
            }
        }
    }
    
    /**
     * 阻塞获取访问许可
     */
    public void acquire() {
        while (!tryAcquire()) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("获取令牌被中断", e);
            }
        }
    }
    
    /**
     * 带超时的阻塞获取访问许可
     * @param timeout 超时时间(毫秒)
     * @return true表示获取成功,false表示超时
     */
    public boolean tryAcquire(long timeout) {
        long endTime = System.currentTimeMillis() + timeout;
        
        while (System.currentTimeMillis() < endTime) {
            if (tryAcquire()) {
                return true;
            }
            
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("获取令牌被中断", e);
            }
        }
        
        return false;
    }
    
    /**
     * 获取当前令牌数
     */
    public long getCurrentTokens() {
        refillTokens();
        return tokens.get();
    }
    
    /**
     * 性能测试
     */
    public void performanceTest() {
        log.info("=== 令牌桶算法性能测试 ===");
        
        TokenBucketRateLimiter limiter = new TokenBucketRateLimiter(100, 100);
        int totalRequests = 1000;
        int allowedCount = 0;
        int rejectedCount = 0;
        
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < totalRequests; i++) {
            if (limiter.tryAcquire()) {
                allowedCount++;
            } else {
                rejectedCount++;
            }
            
            // 模拟请求间隔
            try {
                Thread.sleep(0, 100000); // 0.1ms
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        long endTime = System.currentTimeMillis();
        
        log.info("总请求数: {}, 允许: {}, 拒绝: {}, 耗时: {}ms", 
            totalRequests, allowedCount, rejectedCount, endTime - startTime);
    }
}

/**
 * 分布式令牌桶限流器(基于Redis)
 */
@Component
public class DistributedTokenBucketRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(DistributedTokenBucketRateLimiter.class);
    
    private final StringRedisTemplate redisTemplate;
    private final long capacity;
    private final long refillRateInMillis;
    
    private static final String BUCKET_PREFIX = "token:bucket:";
    private static final String BUCKET_TOKENS_SUFFIX = ":tokens";
    private static final String BUCKET_LAST_REFILL_SUFFIX = ":last_refill";
    
    public DistributedTokenBucketRateLimiter(StringRedisTemplate redisTemplate, 
            long capacity, long refillRatePerSecond) {
        this.redisTemplate = redisTemplate;
        this.capacity = capacity;
        this.refillRateInMillis = 1000L / refillRatePerSecond;
        
        log.info("初始化分布式令牌桶限流器: 容量={}, 补充速率={}/秒", capacity, refillRatePerSecond);
    }
    
    /**
     * 尝试获取访问许可
     * @param key 限流键
     * @return true表示允许,false表示被拒绝
     */
    public boolean tryAcquire(String key) {
        return tryAcquire(key, 1);
    }
    
    /**
     * 尝试获取多个访问许可
     * @param key 限流键
     * @param permits 需要的许可数
     * @return true表示允许,false表示被拒绝
     */
    public boolean tryAcquire(String key, int permits) {
        String tokensKey = BUCKET_PREFIX + key + BUCKET_TOKENS_SUFFIX;
        String lastRefillKey = BUCKET_PREFIX + key + BUCKET_LAST_REFILL_SUFFIX;
        long currentTime = System.currentTimeMillis();
        
        // 使用Lua脚本保证原子性
        String luaScript = 
            "local tokensKey = KEYS[1]\n" +
            "local lastRefillKey = KEYS[2]\n" +
            "local capacity = tonumber(ARGV[1])\n" +
            "local refillRate = tonumber(ARGV[2])\n" +
            "local permits = tonumber(ARGV[3])\n" +
            "local currentTime = tonumber(ARGV[4])\n" +
            "\n" +
            "local tokens = tonumber(redis.call('GET', tokensKey) or capacity)\n" +
            "local lastRefillTime = tonumber(redis.call('GET', lastRefillKey) or currentTime)\n" +
            "\n" +
            "-- 计算应该补充的令牌数\n" +
            "local elapsedTime = currentTime - lastRefillTime\n" +
            "local tokensToAdd = math.floor(elapsedTime / refillRate)\n" +
            "\n" +
            "-- 更新令牌数\n" +
            "tokens = math.min(capacity, tokens + tokensToAdd)\n" +
            "redis.call('SET', lastRefillKey, currentTime)\n" +
            "\n" +
            "-- 检查是否可以获取令牌\n" +
            "if tokens >= permits then\n" +
            "    tokens = tokens - permits\n" +
            "    redis.call('SET', tokensKey, tokens)\n" +
            "    return 1\n" +
            "else\n" +
            "    redis.call('SET', tokensKey, tokens)\n" +
            "    return 0\n" +
            "end";
        
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        Long result = redisTemplate.execute(redisScript, 
            Arrays.asList(tokensKey, lastRefillKey), 
            capacity, refillRateInMillis, permits, currentTime);
        
        boolean allowed = result != null && result == 1;
        
        if (!allowed) {
            log.warn("分布式令牌桶限流: key={}, permits={}", key, permits);
        }
        
        return allowed;
    }
    
    /**
     * 获取当前令牌数
     */
    public long getCurrentTokens(String key) {
        String tokensKey = BUCKET_PREFIX + key + BUCKET_TOKENS_SUFFIX;
        String tokensStr = redisTemplate.opsForValue().get(tokensKey);
        return tokensStr != null ? Long.parseLong(tokensStr) : capacity;
    }
}

令牌桶算法适用场景

场景1:API接口限流

应用场景

  • 保护API不被过载
  • 支持突发流量
  • 灵活配置不同接口的限流策略
/**
 * API接口令牌桶限流器
 */
@Component
public class ApiTokenBucketRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(ApiTokenBucketRateLimiter.class);
    
    private final Map<String, TokenBucketRateLimiter> apiLimiters;
    private final TokenBucketRateLimiter globalLimiter;
    
    public ApiTokenBucketRateLimiter() {
        this.apiLimiters = new ConcurrentHashMap<>();
        
        // 全局限流:10000 QPS,桶容量20000(支持突发)
        this.globalLimiter = new TokenBucketRateLimiter(20000, 10000);
        
        // 各API限流配置
        apiLimiters.put("/api/user/login", new TokenBucketRateLimiter(2000, 1000));
        apiLimiters.put("/api/order/create", new TokenBucketRateLimiter(1000, 500));
        apiLimiters.put("/api/product/list", new TokenBucketRateLimiter(4000, 2000));
        
        log.info("API令牌桶限流器初始化完成");
    }
    
    /**
     * 检查请求是否允许通过
     */
    public boolean allowRequest(String apiPath, String userId) {
        // 1. 检查全局限流
        if (!globalLimiter.tryAcquire()) {
            log.warn("全局限流触发: api={}", apiPath);
            return false;
        }
        
        // 2. 检查API级别限流
        TokenBucketRateLimiter apiLimiter = apiLimiters.get(apiPath);
        if (apiLimiter != null && !apiLimiter.tryAcquire()) {
            log.warn("API限流触发: api={}", apiPath);
            return false;
        }
        
        // 3. 检查用户级别限流
        String userKey = "user:" + userId;
        // 这里可以添加用户级别限流逻辑
        
        return true;
    }
    
    /**
     * 批量请求处理(支持突发)
     */
    public boolean allowBatchRequest(String apiPath, int batchSize) {
        TokenBucketRateLimiter apiLimiter = apiLimiters.get(apiPath);
        if (apiLimiter != null) {
            return apiLimiter.tryAcquire(batchSize);
        }
        return true;
    }
}
场景2:微服务间调用限流

应用场景

  • 保护下游微服务
  • 控制调用速率
  • 支持批量调用
/**
 * 微服务调用令牌桶限流器
 */
@Component
public class MicroserviceCallRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(MicroserviceCallRateLimiter.class);
    
    private final Map<String, TokenBucketRateLimiter> serviceLimiters;
    
    public MicroserviceCallRateLimiter() {
        this.serviceLimiters = new ConcurrentHashMap<>();
        
        // 各微服务限流配置
        serviceLimiters.put("user-service", new TokenBucketRateLimiter(5000, 2000));
        serviceLimiters.put("order-service", new TokenBucketRateLimiter(3000, 1000));
        serviceLimiters.put("product-service", new TokenBucketRateLimiter(8000, 3000));
        
        log.info("微服务调用限流器初始化完成");
    }
    
    /**
     * 调用微服务
     */
    public <T> T callService(String serviceName, Supplier<T> callable) {
        TokenBucketRateLimiter limiter = serviceLimiters.get(serviceName);
        
        if (limiter != null && !limiter.tryAcquire()) {
            log.warn("微服务调用限流: service={}", serviceName);
            throw new RateLimitException("服务调用频率超限,请稍后重试");
        }
        
        try {
            return callable.get();
        } catch (Exception e) {
            log.error("微服务调用异常: service={}", serviceName, e);
            throw new ServiceException("服务调用失败", e);
        }
    }
    
    /**
     * 批量调用微服务
     */
    public <T> List<T> callServiceBatch(String serviceName, List<Supplier<T>> callables) {
        TokenBucketRateLimiter limiter = serviceLimiters.get(serviceName);
        
        if (limiter != null && !limiter.tryAcquire(callables.size())) {
            log.warn("微服务批量调用限流: service={}, size={}", serviceName, callables.size());
            throw new RateLimitException("批量服务调用频率超限");
        }
        
        List<T> results = new ArrayList<>();
        for (Supplier<T> callable : callables) {
            try {
                results.add(callable.get());
            } catch (Exception e) {
                log.error("微服务批量调用异常: service={}", serviceName, e);
            }
        }
        
        return results;
    }
}

令牌桶算法优缺点

优点缺点
支持突发流量实现相对复杂
灵活配置生成速率和容量需要维护令牌计数
平滑限流,长期平均速率恒定突发流量后令牌需要时间恢复
适合需要处理突发流量的场景短期内可能超过限流阈值

三种算法对比

算法特性对比

限流算法对比

计数器算法

漏桶算法

令牌桶算法

固定窗口: 简单但精确度低

滑动窗口: 精确但资源消耗大

恒定输出: 保护下游

无法处理突发

支持突发: 灵活配置

实现复杂度中等

详细对比表

对比维度固定窗口计数器滑动窗口计数器漏桶算法令牌桶算法
实现复杂度简单中等中等中等
限流精确度低(临界突变)
突发流量处理不支持不支持不支持支持
流量平滑性一般
内存占用中等较高(队列)
适用场景简单限流精确限流保护下游系统需要处理突发流量
资源消耗中等较高中等
长期平均速率可控可控恒定可控
短期突发能力

算法选择决策树

选择限流算法

需要处理突发流量?

令牌桶算法

需要精确限流?

资源限制严格?

固定窗口计数器

固定窗口计数器

滑动窗口计数器

需要保护下游系统?

漏桶算法

令牌桶算法

限流架构设计

多层限流架构

客户端请求

CDN/WAF限流

网关层限流

应用层限流

服务层限流

数据库层限流

IP限流

地域限流

防DDoS

全局限流

API限流

用户限流

方法限流

热点数据限流

资源限流

服务间调用限流

下游服务保护

连接池限流

查询限流

分布式限流架构

/**
 * 分布式限流管理器
 */
@Component
public class DistributedRateLimitManager {
    private static final Logger log = LoggerFactory.getLogger(DistributedRateLimitManager.class);
    
    private final StringRedisTemplate redisTemplate;
    private final Map<String, RateLimiterStrategy> strategies;
    
    /**
     * 限流策略接口
     */
    public interface RateLimiterStrategy {
        boolean tryAcquire(String key, int permits);
        long getCurrentTokens(String key);
    }
    
    /**
     * 令牌桶策略
     */
    @Component
    public static class TokenBucketStrategy implements RateLimiterStrategy {
        private final StringRedisTemplate redisTemplate;
        private final long capacity;
        private final long refillRatePerSecond;
        
        public TokenBucketStrategy(StringRedisTemplate redisTemplate, 
                long capacity, long refillRatePerSecond) {
            this.redisTemplate = redisTemplate;
            this.capacity = capacity;
            this.refillRatePerSecond = refillRatePerSecond;
        }
        
        @Override
        public boolean tryAcquire(String key, int permits) {
            // 实现令牌桶逻辑
            return true;
        }
        
        @Override
        public long getCurrentTokens(String key) {
            // 获取当前令牌数
            return 0;
        }
    }
    
    public DistributedRateLimitManager(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.strategies = new ConcurrentHashMap<>();
        
        // 初始化各种限流策略
        strategies.put("token-bucket", new TokenBucketStrategy(redisTemplate, 10000, 1000));
        
        log.info("分布式限流管理器初始化完成");
    }
    
    /**
     * 尝试获取访问许可
     */
    public boolean tryAcquire(String strategy, String key, int permits) {
        RateLimiterStrategy limiter = strategies.get(strategy);
        if (limiter == null) {
            log.error("未找到限流策略: {}", strategy);
            return false;
        }
        
        return limiter.tryAcquire(key, permits);
    }
    
    /**
     * 获取当前令牌数
     */
    public long getCurrentTokens(String strategy, String key) {
        RateLimiterStrategy limiter = strategies.get(strategy);
        if (limiter == null) {
            return 0;
        }
        
        return limiter.getCurrentTokens(key);
    }
}

限流配置管理

/**
 * 限流配置管理器
 */
@Component
public class RateLimitConfigManager {
    private static final Logger log = LoggerFactory.getLogger(RateLimitConfigManager.class);
    
    private final Map<String, RateLimitConfig> configs;
    
    /**
     * 限流配置类
     */
    @Data
    @Builder
    public static class RateLimitConfig {
        private String key;                    // 限流键
        private String strategy;               // 限流策略
        private long capacity;                 // 容量
        private long rate;                     // 速率
        private String dimension;              // 限流维度
        private int priority;                  // 优先级
        private boolean enabled;               // 是否启用
    }
    
    public RateLimitConfigManager() {
        this.configs = new ConcurrentHashMap<>();
        
        // 加载默认配置
        loadDefaultConfigs();
        
        log.info("限流配置管理器初始化完成");
    }
    
    /**
     * 加载默认配置
     */
    private void loadDefaultConfigs() {
        configs.put("global", RateLimitConfig.builder()
            .key("global")
            .strategy("token-bucket")
            .capacity(10000)
            .rate(1000)
            .dimension("global")
            .priority(1)
            .enabled(true)
            .build());
        
        configs.put("api:user:login", RateLimitConfig.builder()
            .key("api:user:login")
            .strategy("token-bucket")
            .capacity(2000)
            .rate(100)
            .dimension("api")
            .priority(2)
            .enabled(true)
            .build());
    }
    
    /**
     * 获取限流配置
     */
    public RateLimitConfig getConfig(String key) {
        return configs.get(key);
    }
    
    /**
     * 更新限流配置
     */
    public void updateConfig(String key, RateLimitConfig config) {
        configs.put(key, config);
        log.info("更新限流配置: key={}", key);
    }
    
    /**
     * 动态调整限流阈值
     */
    public void adjustThreshold(String key, long newRate) {
        RateLimitConfig config = configs.get(key);
        if (config != null) {
            config.setRate(newRate);
            log.info("调整限流阈值: key={}, newRate={}", key, newRate);
        }
    }
}

限流最佳实践

实践1:限流策略设计

设计原则
  1. 分层限流:在不同层级实施限流,形成防护体系
  2. 多维度限流:从IP、用户、API等多个维度限流
  3. 动态调整:根据系统负载动态调整限流阈值
  4. 优雅降级:限流时提供降级服务
/**
 * 分层限流策略
 */
@Component
public class LayeredRateLimitStrategy {
    private static final Logger log = LoggerFactory.getLogger(LayeredRateLimitStrategy.class);
    
    private final TokenBucketRateLimiter globalLimiter;      // 全局限流
    private final TokenBucketRateLimiter apiLimiter;         // API限流
    private final TokenBucketRateLimiter userLimiter;        // 用户限流
    private final TokenBucketRateLimiter ipLimiter;          // IP限流
    
    public LayeredRateLimitStrategy() {
        // 全局限流:10000 QPS,桶容量20000
        this.globalLimiter = new TokenBucketRateLimiter(20000, 10000);
        
        // API限流:1000 QPS,桶容量2000
        this.apiLimiter = new TokenBucketRateLimiter(2000, 1000);
        
        // 用户限流:100 QPS,桶容量200
        this.userLimiter = new TokenBucketRateLimiter(200, 100);
        
        // IP限流:50 QPS,桶容量100
        this.ipLimiter = new TokenBucketRateLimiter(100, 50);
        
        log.info("分层限流策略初始化完成");
    }
    
    /**
     * 检查请求是否允许通过
     */
    public RateLimitResult checkRequest(RequestContext context) {
        // 1. 检查全局限流
        if (!globalLimiter.tryAcquire()) {
            log.warn("全局限流触发");
            return RateLimitResult.rejected("全局限流", "global");
        }
        
        // 2. 检查API限流
        if (!apiLimiter.tryAcquire()) {
            log.warn("API限流触发: api={}", context.getApiPath());
            return RateLimitResult.rejected("API限流", "api");
        }
        
        // 3. 检查用户限流
        if (context.getUserId() != null && !userLimiter.tryAcquire()) {
            log.warn("用户限流触发: userId={}", context.getUserId());
            return RateLimitResult.rejected("用户限流", "user");
        }
        
        // 4. 检查IP限流
        if (context.getClientIp() != null && !ipLimiter.tryAcquire()) {
            log.warn("IP限流触发: ip={}", context.getClientIp());
            return RateLimitResult.rejected("IP限流", "ip");
        }
        
        return RateLimitResult.allowed();
    }
    
    /**
     * 限流结果
     */
    @Data
    @AllArgsConstructor
    public static class RateLimitResult {
        private boolean allowed;
        private String reason;
        private String dimension;
        
        public static RateLimitResult allowed() {
            return new RateLimitResult(true, null, null);
        }
        
        public static RateLimitResult rejected(String reason, String dimension) {
            return new RateLimitResult(false, reason, dimension);
        }
    }
}

实践2:限流监控与告警

/**
 * 限流监控器
 */
@Component
public class RateLimitMonitor {
    private static final Logger log = LoggerFactory.getLogger(RateLimitMonitor.class);
    
    private final AtomicLong totalRequests;
    private final AtomicLong allowedRequests;
    private final AtomicLong rejectedRequests;
    private final ConcurrentHashMap<String, AtomicLong> rejectedByDimension;
    
    private final ScheduledExecutorService monitorExecutor;
    
    public RateLimitMonitor() {
        this.totalRequests = new AtomicLong(0);
        this.allowedRequests = new AtomicLong(0);
        this.rejectedRequests = new AtomicLong(0);
        this.rejectedByDimension = new ConcurrentHashMap<>();
        
        // 启动监控任务
        this.monitorExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread thread = new Thread(r, "rate-limit-monitor");
            thread.setDaemon(true);
            return thread;
        });
        
        this.monitorExecutor.scheduleAtFixedRate(
            this::reportMetrics,
            1, 1, TimeUnit.MINUTES
        );
        
        log.info("限流监控器初始化完成");
    }
    
    /**
     * 记录请求
     */
    public void recordRequest(boolean allowed, String dimension) {
        totalRequests.incrementAndGet();
        
        if (allowed) {
            allowedRequests.incrementAndGet();
        } else {
            rejectedRequests.incrementAndGet();
            
            if (dimension != null) {
                rejectedByDimension.computeIfAbsent(dimension, 
                    k -> new AtomicLong(0)).incrementAndGet();
            }
        }
    }
    
    /**
     * 获取限流统计
     */
    public RateLimitStats getStats() {
        Map<String, Long> rejectedByDim = new HashMap<>();
        rejectedByDimension.forEach((k, v) -> rejectedByDim.put(k, v.get()));
        
        return RateLimitStats.builder()
            .totalRequests(totalRequests.get())
            .allowedRequests(allowedRequests.get())
            .rejectedRequests(rejectedRequests.get())
            .rejectedByDimension(rejectedByDim)
            .build();
    }
    
    /**
     * 报告监控指标
     */
    private void reportMetrics() {
        RateLimitStats stats = getStats();
        
        log.info("=== 限流监控指标 ===");
        log.info("总请求数: {}", stats.getTotalRequests());
        log.info("允许请求数: {}", stats.getAllowedRequests());
        log.info("拒绝请求数: {}", stats.getRejectedRequests());
        log.info("拒绝率: {}%", 
            stats.getTotalRequests() > 0 ? 
            (stats.getRejectedRequests() * 100 / stats.getTotalRequests()) : 0);
        
        // 检查是否需要告警
        if (stats.getRejectedRequests() > 1000) {
            log.warn("限流拒绝请求过多,触发告警: {}", stats.getRejectedRequests());
            // 发送告警通知
            sendAlert(stats);
        }
    }
    
    /**
     * 发送告警
     */
    private void sendAlert(RateLimitStats stats) {
        // 实现告警逻辑
        log.warn("发送限流告警: {}", stats);
    }
    
    /**
     * 限流统计
     */
    @Data
    @Builder
    public static class RateLimitStats {
        private long totalRequests;
        private long allowedRequests;
        private long rejectedRequests;
        private Map<String, Long> rejectedByDimension;
    }
}

实践3:限流降级策略

/**
 * 限流降级处理器
 */
@Component
public class RateLimitFallbackHandler {
    private static final Logger log = LoggerFactory.getLogger(RateLimitFallbackHandler.class);
    
    /**
     * 处理限流降级
     */
    public Object handleFallback(RequestContext context, String dimension) {
        log.info("执行限流降级: dimension={}, api={}", dimension, context.getApiPath());
        
        // 根据不同维度执行不同的降级策略
        switch (dimension) {
            case "global":
                return handleGlobalFallback(context);
            case "api":
                return handleApiFallback(context);
            case "user":
                return handleUserFallback(context);
            case "ip":
                return handleIpFallback(context);
            default:
                return handleDefaultFallback(context);
        }
    }
    
    /**
     * 全局限流降级
     */
    private Object handleGlobalFallback(RequestContext context) {
        log.warn("全局限流降级: 返回服务繁忙提示");
        
        // 返回服务繁忙提示
        return Response.builder()
            .code(503)
            .message("服务繁忙,请稍后重试")
            .timestamp(System.currentTimeMillis())
            .build();
    }
    
    /**
     * API限流降级
     */
    private Object handleApiFallback(RequestContext context) {
        log.warn("API限流降级: api={}", context.getApiPath());
        
        // 返回缓存数据或降级数据
        return getCachedData(context.getApiPath());
    }
    
    /**
     * 用户限流降级
     */
    private Object handleUserFallback(RequestContext context) {
        log.warn("用户限流降级: userId={}", context.getUserId());
        
        // 返回用户限流提示
        return Response.builder()
            .code(429)
            .message("请求过于频繁,请稍后重试")
            .timestamp(System.currentTimeMillis())
            .build();
    }
    
    /**
     * IP限流降级
     */
    private Object handleIpFallback(RequestContext context) {
        log.warn("IP限流降级: ip={}", context.getClientIp());
        
        // 返回验证码或限制提示
        return Response.builder()
            .code(429)
            .message("检测到异常请求,请完成验证")
            .timestamp(System.currentTimeMillis())
            .build();
    }
    
    /**
     * 默认降级
     */
    private Object handleDefaultFallback(RequestContext context) {
        return Response.builder()
            .code(503)
            .message("服务暂时不可用")
            .timestamp(System.currentTimeMillis())
            .build();
    }
    
    /**
     * 获取缓存数据
     */
    private Object getCachedData(String apiPath) {
        // 从缓存获取数据
        return null;
    }
}

限流实战案例

案例1:电商秒杀系统限流

场景描述

  • 秒杀活动期间流量激增
  • 需要保护系统不被冲垮
  • 保证核心功能可用

限流策略

/**
 * 秒杀系统限流器
 */
@Component
public class SeckillRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(SeckillRateLimiter.class);
    
    private final TokenBucketRateLimiter globalLimiter;
    private final ConcurrentHashMap<String, TokenBucketRateLimiter> productLimiters;
    private final ConcurrentHashMap<String, TokenBucketRateLimiter> userLimiters;
    
    public SeckillRateLimiter() {
        // 全局限流:50000 QPS,桶容量100000(支持突发)
        this.globalLimiter = new TokenBucketRateLimiter(100000, 50000);
        
        this.productLimiters = new ConcurrentHashMap<>();
        this.userLimiters = new ConcurrentHashMap<>();
        
        log.info("秒杀系统限流器初始化完成");
    }
    
    /**
     * 检查秒杀请求是否允许
     */
    public boolean allowSeckillRequest(String productId, String userId) {
        // 1. 检查全局限流
        if (!globalLimiter.tryAcquire()) {
            log.warn("秒杀全局限流触发");
            return false;
        }
        
        // 2. 检查商品维度限流
        TokenBucketRateLimiter productLimiter = productLimiters.computeIfAbsent(
            productId, 
            k -> new TokenBucketRateLimiter(5000, 1000)
        );
        
        if (!productLimiter.tryAcquire()) {
            log.warn("秒杀商品限流触发: productId={}", productId);
            return false;
        }
        
        // 3. 检查用户维度限流
        TokenBucketRateLimiter userLimiter = userLimiters.computeIfAbsent(
            userId,
            k -> new TokenBucketRateLimiter(10, 1)  // 每用户每秒1次
        );
        
        if (!userLimiter.tryAcquire()) {
            log.warn("秒杀用户限流触发: userId={}", userId);
            return false;
        }
        
        return true;
    }
    
    /**
     * 预热限流器
     */
    public void warmUp(String productId, long expectedQPS) {
        TokenBucketRateLimiter limiter = productLimiters.computeIfAbsent(
            productId,
            k -> new TokenBucketRateLimiter(expectedQPS * 2, expectedQPS)
        );
        log.info("预热秒杀限流器: productId={}, expectedQPS={}", productId, expectedQPS);
    }
}

效果

  • 全局限流保护系统不被冲垮
  • 商品维度限流保证热门商品公平访问
  • 用户维度限流防止刷单
  • 支持突发流量,提升用户体验

案例2:微博热点事件限流

场景描述

  • 明星公布恋情等热点事件
  • 流量从50万QPS激增到500万QPS
  • 系统容量200万QPS

限流策略

/**
 * 微博热点事件限流器
 */
@Component
public class WeiboHotTopicRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(WeiboHotTopicRateLimiter.class);
    
    private final TokenBucketRateLimiter globalLimiter;
    private final ConcurrentHashMap<String, TokenBucketRateLimiter> topicLimiters;
    private final ConcurrentHashMap<String, TokenBucketRateLimiter> userLimiters;
    private final ConcurrentHashMap<String, TokenBucketRateLimiter> regionLimiters;
    
    public WeiboHotTopicRateLimiter() {
        // 全局限流:200万QPS,桶容量300万(支持突发)
        this.globalLimiter = new TokenBucketRateLimiter(3000000, 2000000);
        
        this.topicLimiters = new ConcurrentHashMap<>();
        this.userLimiters = new ConcurrentHashMap<>();
        this.regionLimiters = new ConcurrentHashMap<>();
        
        log.info("微博热点事件限流器初始化完成");
    }
    
    /**
     * 检查请求是否允许
     */
    public boolean allowRequest(String topicId, String userId, String region) {
        // 1. 检查全局限流
        if (!globalLimiter.tryAcquire()) {
            log.warn("全局限流触发");
            return false;
        }
        
        // 2. 检查话题维度限流
        if (topicId != null) {
            TokenBucketRateLimiter topicLimiter = topicLimiters.computeIfAbsent(
                topicId,
                k -> new TokenBucketRateLimiter(500000, 100000)
            );
            
            if (!topicLimiter.tryAcquire()) {
                log.warn("话题限流触发: topicId={}", topicId);
                return false;
            }
        }
        
        // 3. 检查用户维度限流
        if (userId != null) {
            TokenBucketRateLimiter userLimiter = userLimiters.computeIfAbsent(
                userId,
                k -> new TokenBucketRateLimiter(100, 10)
            );
            
            if (!userLimiter.tryAcquire()) {
                log.warn("用户限流触发: userId={}", userId);
                return false;
            }
        }
        
        // 4. 检查地域维度限流
        if (region != null) {
            TokenBucketRateLimiter regionLimiter = regionLimiters.computeIfAbsent(
                region,
                k -> new TokenBucketRateLimiter(200000, 50000)
            );
            
            if (!regionLimiter.tryAcquire()) {
                log.warn("地域限流触发: region={}", region);
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * 动态调整限流阈值
     */
    public void adjustThreshold(String dimension, String key, long newRate) {
        switch (dimension) {
            case "global":
                log.info("调整全局限流阈值: newRate={}", newRate);
                // 重新创建全局限流器
                break;
            case "topic":
                TokenBucketRateLimiter topicLimiter = topicLimiters.get(key);
                if (topicLimiter != null) {
                    log.info("调整话题限流阈值: topicId={}, newRate={}", key, newRate);
                }
                break;
            case "user":
                TokenBucketRateLimiter userLimiter = userLimiters.get(key);
                if (userLimiter != null) {
                    log.info("调整用户限流阈值: userId={}, newRate={}", key, newRate);
                }
                break;
            case "region":
                TokenBucketRateLimiter regionLimiter = regionLimiters.get(key);
                if (regionLimiter != null) {
                    log.info("调整地域限流阈值: region={}, newRate={}", key, newRate);
                }
                break;
        }
    }
}

限流策略分析

维度平时阈值突发阈值说明
全局50万QPS200万QPS保护系统不被冲垮
话题1万QPS10万QPS热门话题限流
用户10 QPS10 QPS防止刷屏
地域5万QPS5万QPS地域流量控制

案例3:API网关综合限流

场景描述

  • 多个API接口
  • 不同接口不同限流策略
  • 需要保护后端服务

限流策略

/**
 * API网关综合限流器
 */
@Component
public class ApiGatewayComprehensiveRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(ApiGatewayComprehensiveRateLimiter.class);
    
    private final TokenBucketRateLimiter globalLimiter;
    private final Map<String, ApiRateLimitConfig> apiConfigs;
    private final ConcurrentHashMap<String, TokenBucketRateLimiter> apiLimiters;
    private final ConcurrentHashMap<String, TokenBucketRateLimiter> userLimiters;
    private final ConcurrentHashMap<String, TokenBucketRateLimiter> ipLimiters;
    
    /**
     * API限流配置
     */
    @Data
    @Builder
    public static class ApiRateLimitConfig {
        private String apiPath;
        private long capacity;
        private long rate;
        private boolean enableUserLimit;
        private boolean enableIpLimit;
    }
    
    public ApiGatewayComprehensiveRateLimiter() {
        // 全局限流:10000 QPS,桶容量20000
        this.globalLimiter = new TokenBucketRateLimiter(20000, 10000);
        
        this.apiConfigs = new ConcurrentHashMap<>();
        this.apiLimiters = new ConcurrentHashMap<>();
        this.userLimiters = new ConcurrentHashMap<>();
        this.ipLimiters = new ConcurrentHashMap<>();
        
        // 加载API限流配置
        loadApiConfigs();
        
        log.info("API网关综合限流器初始化完成");
    }
    
    /**
     * 加载API限流配置
     */
    private void loadApiConfigs() {
        // 用户登录API
        apiConfigs.put("/api/user/login", ApiRateLimitConfig.builder()
            .apiPath("/api/user/login")
            .capacity(2000)
            .rate(1000)
            .enableUserLimit(true)
            .enableIpLimit(true)
            .build());
        
        // 订单创建API
        apiConfigs.put("/api/order/create", ApiRateLimitConfig.builder()
            .apiPath("/api/order/create")
            .capacity(1000)
            .rate(500)
            .enableUserLimit(true)
            .enableIpLimit(false)
            .build());
        
        // 商品列表API
        apiConfigs.put("/api/product/list", ApiRateLimitConfig.builder()
            .apiPath("/api/product/list")
            .capacity(4000)
            .rate(2000)
            .enableUserLimit(false)
            .enableIpLimit(true)
            .build());
    }
    
    /**
     * 检查请求是否允许
     */
    public RateLimitResult checkRequest(RequestContext context) {
        String apiPath = context.getApiPath();
        String userId = context.getUserId();
        String clientIp = context.getClientIp();
        
        // 1. 检查全局限流
        if (!globalLimiter.tryAcquire()) {
            log.warn("全局限流触发: api={}", apiPath);
            return RateLimitResult.rejected("全局限流", "global");
        }
        
        // 2. 检查API级别限流
        ApiRateLimitConfig config = apiConfigs.get(apiPath);
        if (config != null) {
            TokenBucketRateLimiter apiLimiter = apiLimiters.computeIfAbsent(
                apiPath,
                k -> new TokenBucketRateLimiter(config.getCapacity(), config.getRate())
            );
            
            if (!apiLimiter.tryAcquire()) {
                log.warn("API限流触发: api={}", apiPath);
                return RateLimitResult.rejected("API限流", "api");
            }
            
            // 3. 检查用户级别限流
            if (config.isEnableUserLimit() && userId != null) {
                TokenBucketRateLimiter userLimiter = userLimiters.computeIfAbsent(
                    userId,
                    k -> new TokenBucketRateLimiter(100, 10)
                );
                
                if (!userLimiter.tryAcquire()) {
                    log.warn("用户限流触发: userId={}", userId);
                    return RateLimitResult.rejected("用户限流", "user");
                }
            }
            
            // 4. 检查IP级别限流
            if (config.isEnableIpLimit() && clientIp != null) {
                TokenBucketRateLimiter ipLimiter = ipLimiters.computeIfAbsent(
                    clientIp,
                    k -> new TokenBucketRateLimiter(50, 5)
                );
                
                if (!ipLimiter.tryAcquire()) {
                    log.warn("IP限流触发: ip={}", clientIp);
                    return RateLimitResult.rejected("IP限流", "ip");
                }
            }
        }
        
        return RateLimitResult.allowed();
    }
    
    /**
     * 限流结果
     */
    @Data
    @AllArgsConstructor
    public static class RateLimitResult {
        private boolean allowed;
        private String reason;
        private String dimension;
        
        public static RateLimitResult allowed() {
            return new RateLimitResult(true, null, null);
        }
        
        public static RateLimitResult rejected(String reason, String dimension) {
            return new RateLimitResult(false, reason, dimension);
        }
    }
}

限流工具与框架

开源限流框架

1. Guava RateLimiter

Google Guava提供的令牌桶限流器实现。

import com.google.common.util.concurrent.RateLimiter;

/**
 * Guava RateLimiter示例
 */
public class GuavaRateLimiterExample {
    private final RateLimiter rateLimiter;
    
    public GuavaRateLimiterExample(double permitsPerSecond) {
        // 创建令牌桶限流器,每秒生成permitsPerSecond个令牌
        this.rateLimiter = RateLimiter.create(permitsPerSecond);
    }
    
    /**
     * 尝试获取许可
     */
    public boolean tryAcquire() {
        return rateLimiter.tryAcquire();
    }
    
    /**
     * 尝试获取许可(带超时)
     */
    public boolean tryAcquire(int timeout, TimeUnit unit) {
        return rateLimiter.tryAcquire(timeout, unit);
    }
    
    /**
     * 阻塞获取许可
     */
    public void acquire() {
        rateLimiter.acquire();
    }
    
    /**
     * 获取多个许可
     */
    public void acquire(int permits) {
        rateLimiter.acquire(permits);
    }
}
2. Sentinel

阿里巴巴开源的流量控制框架。

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;

/**
 * Sentinel限流示例
 */
public class SentinelRateLimiterExample {
    
    /**
     * 初始化限流规则
     */
    public void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        
        // 创建限流规则
        FlowRule rule = new FlowRule();
        rule.setResource("hello");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(20);  // 限流阈值:20 QPS
        
        rules.add(rule);
        
        // 加载规则
        FlowRuleManager.loadRules(rules);
    }
    
    /**
     * 使用限流
     */
    public void doSomething() {
        try (Entry entry = SphU.entry("hello")) {
            // 被保护的逻辑
            System.out.println("Hello world");
        } catch (BlockException ex) {
            // 被限流
            System.out.println("Blocked!");
        }
    }
}
3. Resilience4j

轻量级的容错库,支持限流、熔断等功能。

import io.github.resilience4j.ratelimiter.RateLimiter;
import io.github.resilience4j.ratelimiter.RateLimiterConfig;

import java.time.Duration;

/**
 * Resilience4j限流示例
 */
public class Resilience4jRateLimiterExample {
    private final RateLimiter rateLimiter;
    
    public Resilience4jRateLimiterExample() {
        // 配置限流器
        RateLimiterConfig config = RateLimiterConfig.custom()
            .limitForPeriod(10)  // 周期内允许的请求数
            .limitRefreshPeriod(Duration.ofSeconds(1))  // 刷新周期
            .timeoutDuration(Duration.ofMillis(100))  // 等待超时
            .build();
        
        // 创建限流器
        this.rateLimiter = RateLimiter.of("myLimiter", config);
    }
    
    /**
     * 使用限流器
     */
    public void doSomething() {
        // 获取许可
        if (rateLimiter.acquirePermission()) {
            // 执行业务逻辑
            System.out.println("Request allowed");
        } else {
            // 被限流
            System.out.println("Request blocked");
        }
    }
}

分布式限流方案

基于Redis的分布式限流
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Arrays;

/**
 * Redis分布式限流器
 */
@Component
public class RedisDistributedRateLimiter {
    private static final Logger log = LoggerFactory.getLogger(RedisDistributedRateLimiter.class);
    
    private final StringRedisTemplate redisTemplate;
    
    // 限流Lua脚本
    private static final String RATE_LIMIT_SCRIPT =
        "local key = KEYS[1]\n" +
        "local limit = tonumber(ARGV[1])\n" +
        "local expire = tonumber(ARGV[2])\n" +
        "local current = tonumber(redis.call('GET', key) or 0)\n" +
        "\n" +
        "if current + 1 > limit then\n" +
        "    return 0\n" +
        "else\n" +
        "    redis.call('INCR', key)\n" +
        "    redis.call('EXPIRE', key, expire)\n" +
        "    return 1\n" +
        "end";
    
    public RedisDistributedRateLimiter(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    /**
     * 尝试获取许可
     * @param key 限流键
     * @param limit 限流阈值
     * @param expire 过期时间(秒)
     * @return true表示允许,false表示被拒绝
     */
    public boolean tryAcquire(String key, long limit, long expire) {
        DefaultRedisScript<Long> script = new DefaultRedisScript<>(RATE_LIMIT_SCRIPT, Long.class);
        
        Long result = redisTemplate.execute(
            script,
            Arrays.asList(key),
            String.valueOf(limit),
            String.valueOf(expire)
        );
        
        boolean allowed = result != null && result == 1;
        
        if (!allowed) {
            log.warn("Redis限流: key={}, limit={}", key, limit);
        }
        
        return allowed;
    }
}

总结

限流法则是现代分布式系统架构中不可或缺的核心法则之一。通过合理运用计数器、漏桶、令牌桶等限流算法,我们能够有效控制系统流量,保护系统稳定性,提升用户体验。

核心原则

  1. 流量控制优先:在设计系统时就要考虑限流策略,而不是事后补救
  2. 分层防护:在不同层级实施限流,形成多道防护屏障
  3. 多维度限流:从全局、API、用户、IP等多个维度进行限流
  4. 动态调整:根据系统负载和业务情况动态调整限流阈值
  5. 优雅降级:限流时提供降级服务,保证核心功能可用

算法选择指南

场景推荐算法原因
简单限流固定窗口计数器实现简单,资源消耗低
精确限流滑动窗口计数器精确度高,无临界突变
保护下游漏桶算法流量平滑,输出恒定
突发流量令牌桶算法支持突发,灵活配置
分布式场景Redis令牌桶分布式支持,原子性保证

实施建议

  1. 容量规划:提前进行容量规划,确定合理的限流阈值
  2. 监控告警:建立完善的监控告警机制,及时发现异常
  3. 压力测试:定期进行压力测试,验证限流策略的有效性
  4. 文档规范:建立限流配置文档,便于团队协作
  5. 持续优化:根据业务发展和系统变化持续优化限流策略

关键技术点

  1. 原子性保证:使用CAS、分布式锁、Lua脚本等保证限流操作的原子性
  2. 性能优化:选择合适的数据结构和算法,降低限流对性能的影响
  3. 内存管理:合理设计限流器的内存占用,避免内存泄漏
  4. 线程安全:确保限流器在多线程环境下的正确性
  5. 异常处理:完善异常处理机制,避免限流器本身成为瓶颈

限流不是目的,而是手段。通过合理的限流策略,我们能够在保障系统稳定性的同时,为用户提供更好的服务体验。在实际应用中,需要根据具体的业务场景和系统特点,选择合适的限流算法和策略,并持续优化调整,以达到最佳的效果。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值