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. 排队等待:保证请求不丢失

选择建议

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

相关推荐
s090713625 分钟前
Xilinx FPGA 中ADC 数据下变频+ CIC 滤波
算法·fpga开发·fpga·zynq
TL滕1 小时前
从0开始学算法——第十二天(KMP算法练习)
笔记·学习·算法
Math_teacher_fan1 小时前
第二篇:核心几何工具类详解
人工智能·算法
汉克老师1 小时前
CCF-NOI2025第二试题目与解析(第二题、集合(set))
c++·算法·noi·子集卷积·sos dp·mod 异常
mit6.8242 小时前
presum|
算法
不穿格子的程序员2 小时前
从零开始写算法——链表篇2:从“回文”到“环形”——链表双指针技巧的深度解析
数据结构·算法·链表·回文链表·环形链表
guygg882 小时前
基于Matlab的压缩感知信道估计算法实现
开发语言·算法·matlab
诺....3 小时前
C语言不确定循环会影响输入输出缓冲区的刷新
c语言·数据结构·算法