Sentinel 限流算法详解

文章目录

    • [一、Sentinel 限流算法概述](#一、Sentinel 限流算法概述)
    • [二、滑动窗口算法(Sliding Window)](#二、滑动窗口算法(Sliding Window))
      • [1. 算法原理](#1. 算法原理)
      • [2. 实现机制](#2. 实现机制)
      • [3. 特点](#3. 特点)
      • [4. 代码示例](#4. 代码示例)
    • [三、令牌桶算法(Token Bucket)](#三、令牌桶算法(Token Bucket))
      • [1. 算法原理](#1. 算法原理)
      • [2. 实现机制](#2. 实现机制)
      • [3. 特点](#3. 特点)
      • [4. 代码示例](#4. 代码示例)
    • [四、漏桶算法(Leaky Bucket)](#四、漏桶算法(Leaky Bucket))
      • [1. 算法原理](#1. 算法原理)
      • [2. 实现机制](#2. 实现机制)
      • [3. 特点](#3. 特点)
      • [4. 代码示例](#4. 代码示例)
    • [五、预热限流算法(Warm Up)](#五、预热限流算法(Warm Up))
      • [1. 算法原理](#1. 算法原理)
      • [2. 实现机制](#2. 实现机制)
      • [3. 特点](#3. 特点)
      • [4. 代码示例](#4. 代码示例)
    • [六、排队等待算法(Rate Limiter)](#六、排队等待算法(Rate Limiter))
      • [1. 算法原理](#1. 算法原理)
      • [2. 实现机制](#2. 实现机制)
      • [3. 特点](#3. 特点)
      • [4. 代码示例](#4. 代码示例)
    • [七、Sentinel 中的实际应用](#七、Sentinel 中的实际应用)
      • [1. 流控规则配置](#1. 流控规则配置)
      • [2. 代码中使用](#2. 代码中使用)
    • 八、算法对比
    • 九、常见问题
      • [Q1: Sentinel 默认使用什么算法?](#Q1: Sentinel 默认使用什么算法?)
      • [Q2: 令牌桶和漏桶的区别?](#Q2: 令牌桶和漏桶的区别?)
      • [Q3: 如何选择限流算法?](#Q3: 如何选择限流算法?)
      • [Q4: 预热限流的作用?](#Q4: 预热限流的作用?)
    • 十、总结

一、Sentinel 限流算法概述

Sentinel 提供了多种限流算法,主要包括:

  1. 滑动窗口算法(默认)
  2. 令牌桶算法
  3. 漏桶算法
  4. 预热限流算法
  5. 排队等待算法

二、滑动窗口算法(Sliding Window)

1. 算法原理

滑动窗口是 Sentinel 的默认限流算法,通过时间窗口内的请求数来控制流量。

2. 实现机制

复制代码
时间轴:0s    1s    2s    3s    4s    5s
窗口1: [====]
窗口2:      [====]
窗口3:           [====]
窗口4:                [====]

每个窗口统计该时间段的请求数

3. 特点

  • 时间窗口:将时间分成多个固定大小的窗口(如 1 秒)
  • 请求计数:每个窗口内统计请求数量
  • 滑动计算:当前窗口的请求数超过阈值时触发限流
  • 精确控制:可以精确控制每秒的请求数

4. 代码示例

java 复制代码
// Sentinel 滑动窗口实现(简化版)
public class SlidingWindow {
    
    private final int windowSize;  // 窗口大小(秒)
    private final int threshold;   // 阈值(每秒请求数)
    private final AtomicInteger[] counters;  // 每个窗口的计数器
    
    public boolean tryAcquire() {
        long currentTime = System.currentTimeMillis() / 1000;
        int windowIndex = (int) (currentTime % windowSize);
        
        // 获取当前窗口的计数器
        AtomicInteger counter = counters[windowIndex];
        
        // 检查是否超过阈值
        if (counter.get() >= threshold) {
            return false;  // 限流
        }
        
        // 增加计数
        counter.incrementAndGet();
        return true;
    }
}

三、令牌桶算法(Token Bucket)

1. 算法原理

令牌桶以固定速率生成令牌,请求需要获取令牌才能通过。

2. 实现机制

复制代码
令牌桶:
┌─────────────┐
│  令牌数量   │ ← 固定速率生成令牌(如每秒 10 个)
│     10      │
│             │
│  容量:20   │
└─────────────┘
     ↓
请求到达 → 消耗 1 个令牌 → 通过
         → 没有令牌 → 限流

3. 特点

  • 固定速率:以固定速率生成令牌(如每秒 10 个)
  • 桶容量:令牌桶有最大容量,超过容量后不再生成
  • 突发流量:允许短时间内的突发流量(消耗积累的令牌)
  • 平滑限流:长期平均速率受限于令牌生成速率

4. 代码示例

java 复制代码
// 令牌桶算法实现(简化版)
public class TokenBucket {
    
    private final int capacity;      // 桶容量
    private final int refillRate;    // 令牌生成速率(每秒)
    private volatile int tokens;     // 当前令牌数
    private volatile long lastRefillTime;  // 上次补充令牌的时间
    
    public synchronized boolean tryAcquire() {
        // 1. 补充令牌
        refillTokens();
        
        // 2. 检查是否有令牌
        if (tokens > 0) {
            tokens--;
            return true;
        }
        
        return false;  // 没有令牌,限流
    }
    
    private void refillTokens() {
        long currentTime = System.currentTimeMillis();
        long elapsedTime = currentTime - lastRefillTime;
        
        // 计算应该补充的令牌数
        int tokensToAdd = (int) (elapsedTime * refillRate / 1000);
        
        if (tokensToAdd > 0) {
            tokens = Math.min(capacity, tokens + tokensToAdd);
            lastRefillTime = currentTime;
        }
    }
}

四、漏桶算法(Leaky Bucket)

1. 算法原理

漏桶以固定速率处理请求,超出容量的请求会被丢弃或排队。

2. 实现机制

复制代码
漏桶:
请求 → ┌─────────────┐ → 固定速率流出(如每秒 5 个)
       │  请求队列   │
       │     10      │
       │  容量:20   │
       └─────────────┘
            ↓
        固定速率处理

3. 特点

  • 固定速率:以固定速率处理请求
  • 桶容量:漏桶有最大容量,超过容量后请求被丢弃
  • 平滑输出:输出速率恒定,不会出现突发
  • 适合场景:需要严格控制输出速率的场景

4. 代码示例

java 复制代码
// 漏桶算法实现(简化版)
public class LeakyBucket {
    
    private final int capacity;      // 桶容量
    private final int leakRate;      // 漏出速率(每秒)
    private volatile int requests;   // 当前请求数
    private volatile long lastLeakTime;  // 上次漏出的时间
    
    public synchronized boolean tryAcquire() {
        // 1. 漏出请求
        leakRequests();
        
        // 2. 检查是否有容量
        if (requests < capacity) {
            requests++;
            return true;
        }
        
        return false;  // 桶满了,限流
    }
    
    private void leakRequests() {
        long currentTime = System.currentTimeMillis();
        long elapsedTime = currentTime - lastLeakTime;
        
        // 计算应该漏出的请求数
        int requestsToLeak = (int) (elapsedTime * leakRate / 1000);
        
        if (requestsToLeak > 0) {
            requests = Math.max(0, requests - requestsToLeak);
            lastLeakTime = currentTime;
        }
    }
}

五、预热限流算法(Warm Up)

1. 算法原理

预热限流在系统启动时逐步提高限流阈值,避免冷启动时的高并发冲击。

2. 实现机制

复制代码
时间轴:0s    5s    10s   15s   20s
阈值:  100 → 200 → 300 → 400 → 500(最终阈值)

逐步提高限流阈值,让系统逐步适应流量

3. 特点

  • 逐步提高:限流阈值从低到高逐步提高
  • 预热时间:在预热时间内逐步达到最终阈值
  • 保护系统:避免系统启动时的高并发冲击
  • 适合场景:系统冷启动、长时间低流量后突然高流量

4. 代码示例

java 复制代码
// 预热限流算法实现(简化版)
public class WarmUpRateLimiter {
    
    private final int finalThreshold;  // 最终阈值
    private final long warmUpPeriod;   // 预热时间(毫秒)
    private final long startTime;      // 启动时间
    
    public int getCurrentThreshold() {
        long elapsedTime = System.currentTimeMillis() - startTime;
        
        if (elapsedTime >= warmUpPeriod) {
            // 预热完成,使用最终阈值
            return finalThreshold;
        }
        
        // 预热阶段,逐步提高阈值
        double factor = (double) elapsedTime / warmUpPeriod;
        return (int) (finalThreshold * factor);
    }
}

六、排队等待算法(Rate Limiter)

1. 算法原理

排队等待允许请求排队等待,而不是直接拒绝,适合需要保证请求不丢失的场景。

2. 实现机制

复制代码
请求队列:
请求1 → ┌─────────────┐ → 处理
请求2 → │  等待队列   │ → 处理
请求3 → │     5       │ → 处理
请求4 → │  容量:10   │ → 处理
请求5 → └─────────────┘ → 处理

3. 特点

  • 排队等待:请求可以排队等待,而不是直接拒绝
  • 队列容量:队列有最大容量,超过容量后请求被拒绝
  • 保证处理:适合需要保证请求不丢失的场景
  • 延迟增加:请求可能需要等待,响应时间增加

4. 代码示例

java 复制代码
// 排队等待算法实现(简化版)
public class RateLimiter {
    
    private final int maxQueueSize;  // 最大队列容量
    private final BlockingQueue<Request> queue;  // 请求队列
    private final int processRate;   // 处理速率(每秒)
    
    public boolean tryAcquire(Request request) {
        // 尝试加入队列
        if (queue.size() < maxQueueSize) {
            queue.offer(request);
            return true;
        }
        
        return false;  // 队列满了,拒绝
    }
    
    // 后台线程处理队列中的请求
    private void processQueue() {
        while (true) {
            try {
                Request request = queue.take();
                processRequest(request);
                Thread.sleep(1000 / processRate);  // 控制处理速率
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

七、Sentinel 中的实际应用

1. 流控规则配置

java 复制代码
// Sentinel 流控规则
FlowRule rule = new FlowRule();
rule.setResource("userService");  // 资源名称
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);  // 限流维度:QPS
rule.setCount(100);  // 阈值:每秒 100 个请求
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);  // 默认:直接拒绝

// 预热限流
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
rule.setWarmUpPeriodSec(10);  // 预热时间:10 秒

// 排队等待
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
rule.setMaxQueueingTimeMs(500);  // 最大等待时间:500 毫秒

2. 代码中使用

java 复制代码
@Service
public class UserService {
    
    @SentinelResource(
        value = "userService",
        blockHandler = "handleBlock"
    )
    public User getUser(Long id) {
        return userRepository.findById(id);
    }
    
    // 限流降级方法
    public User handleBlock(Long id, BlockException e) {
        return new User();  // 返回默认值
    }
}

八、算法对比

算法 特点 适用场景 优点 缺点
滑动窗口 固定时间窗口内限制请求数 精确控制 QPS 实现简单,精确控制 窗口边界可能突发
令牌桶 固定速率生成令牌 允许突发流量 允许突发,平滑限流 实现较复杂
漏桶 固定速率处理请求 严格控制输出速率 输出速率恒定 不允许突发
预热限流 逐步提高阈值 系统冷启动 保护系统 预热期间限流较严格
排队等待 请求排队等待 保证请求不丢失 不丢失请求 响应时间增加

九、常见问题

Q1: Sentinel 默认使用什么算法?

A : 滑动窗口算法,通过时间窗口内的请求数来控制流量。

Q2: 令牌桶和漏桶的区别?

A:

  • 令牌桶:固定速率生成令牌,允许突发流量
  • 漏桶:固定速率处理请求,不允许突发

Q3: 如何选择限流算法?

A:

  • 精确控制 QPS:滑动窗口
  • 允许突发流量:令牌桶
  • 严格控制输出:漏桶
  • 系统冷启动:预热限流
  • 保证不丢失:排队等待

Q4: 预热限流的作用?

A: 在系统启动时逐步提高限流阈值,避免冷启动时的高并发冲击,保护系统稳定运行。


十、总结

核心算法

  1. 滑动窗口:Sentinel 默认算法,精确控制 QPS
  2. 令牌桶:允许突发流量,适合流量波动大的场景
  3. 漏桶:严格控制输出速率,适合需要平滑输出的场景
  4. 预热限流:保护系统冷启动
  5. 排队等待:保证请求不丢失

选择建议

  • 一般场景:使用滑动窗口(默认)
  • 允许突发:使用令牌桶
  • 严格控制:使用漏桶
  • 冷启动:使用预热限流
  • 保证不丢失:使用排队等待

相关推荐
千金裘换酒2 小时前
LeetCode 移动零元素 快慢指针
算法·leetcode·职场和发展
wm10433 小时前
机器学习第二讲 KNN算法
人工智能·算法·机器学习
NAGNIP3 小时前
一文搞懂机器学习线性代数基础知识!
算法
NAGNIP3 小时前
机器学习入门概述一览
算法
iuu_star3 小时前
C语言数据结构-顺序查找、折半查找
c语言·数据结构·算法
Yzzz-F3 小时前
P1558 色板游戏 [线段树 + 二进制状态压缩 + 懒标记区间重置]
算法
漫随流水4 小时前
leetcode算法(515.在每个树行中找最大值)
数据结构·算法·leetcode·二叉树
mit6.8244 小时前
dfs|前后缀分解
算法
扫地的小何尚4 小时前
NVIDIA RTX PC开源AI工具升级:加速LLM和扩散模型的性能革命
人工智能·python·算法·开源·nvidia·1024程序员节
千金裘换酒6 小时前
LeetCode反转链表
算法·leetcode·链表