稳定性保障:限流降级深度解析 —— Sentinel滑动窗口算法与令牌桶实现

稳定性保障:限流降级深度解析 ------ Sentinel滑动窗口算法与令牌桶实现

目录

  1. 稳定性保障体系概览
  2. 限流算法原理
  3. Sentinel滑动窗口算法详解
  4. 令牌桶算法实战
  5. Sentinel架构与源码解析
  6. 企业级降级策略
  7. 生产环境最佳实践

1. 稳定性保障体系概览

1.1 微服务稳定性威胁矩阵

复制代码
┌─────────────────────────────────────────────────────────────┐
│                     稳定性威胁全景图                          │
├─────────────────────────────────────────────────────────────┤
│  流量突增          依赖故障          资源耗尽          连锁反应   │
│     │                │                │                │     │
│     ▼                ▼                ▼                ▼     │
│ ┌────────┐     ┌────────┐     ┌────────┐     ┌────────┐  │
│ │秒杀/抢购│     │DB宕机  │     │内存泄漏 │     │雪崩效应│  │
│ │DDoS攻击│     │RPC超时 │     │线程池满 │     │级联故障│  │
│ └────────┘     └────────┘     └────────┘     └────────┘  │
│     │                │                │                │     │
│     └────────────────┴────────────────┴────────────────┘     │
│                          │                                 │
│                          ▼                                 │
│              ┌─────────────────────┐                       │
│              │   限流 + 降级 + 熔断   │                     │
│              │   负载均衡 + 隔离      │                     │
│              └─────────────────────┘                       │
└─────────────────────────────────────────────────────────────┘

1.2 稳定性保障三板斧

手段 目标 触发条件 恢复策略
限流(Rate Limiting) 保护系统不被打垮 QPS/并发数超过阈值 自动,流量下降后恢复
降级(Degradation) 保证核心功能可用 依赖故障或超时 依赖恢复后自动恢复
熔断(Circuit Breaking) 防止故障扩散 错误率/慢调用比例超标 半开探测成功后恢复

2. 限流算法原理

2.1 四种经典限流算法对比

算法 核心思想 优点 缺点 适用场景
固定窗口 按时间分片计数 实现简单 边界突发流量(临界问题) 简单统计
滑动窗口 细粒度时间分片 平滑精准 内存占用较高 实时监控
令牌桶 匀速产生令牌,消费需获取 允许突发,平滑限流 实现较复杂 网关入口
漏桶 匀速处理请求,溢出丢弃 绝对平滑,无突发 无法应对突发流量 下游保护

2.2 算法可视化对比

复制代码
时间轴: 0s    1s    2s    3s    4s    5s
        │     │     │     │     │     │
请求:   ▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░  固定窗口(临界突发)
        │     │     │     │     │     │
        ▓▓▓░░▓▓▓░░▓▓▓░░▓▓▓░░▓▓▓░░▓▓▓░  滑动窗口(平滑)
        │     │     │     │     │     │
        ▓▓▓▓▓░░░░░░░░░░▓▓▓▓▓░░░░░░░░░  令牌桶(允许突发)
        │     │     │     │     │     │
        ▓░░░░▓░░░░▓░░░░▓░░░░▓░░░░▓░░░░  漏桶(绝对平滑)

3. Sentinel滑动窗口算法详解

3.1 滑动窗口核心设计

Sentinel采用滑动时间窗口算法实现精准流量控制,解决固定窗口的临界突发问题。

3.1.1 数据结构
java 复制代码
// Sentinel滑动窗口核心数据结构
public class LeapArray<T> {
    // 时间窗口大小(毫秒),默认1000ms
    protected int windowLengthInMs;
    // 采样窗口个数,默认2个(细粒度控制)
    protected int sampleCount;
    // 整个时间跨度 = windowLengthInMs * sampleCount
    protected int intervalInMs;
    
    // 底层存储:循环数组,避免频繁创建对象
    protected final AtomicReferenceArray<WindowWrap<T>> array;
    
    // 每个窗口包装类
    public static class WindowWrap<T> {
        private final long windowStart;  // 窗口开始时间
        private final long windowLength; // 窗口长度
        private T value;                 // 统计数据(MetricBucket)
    }
}
3.1.2 窗口计算逻辑
java 复制代码
public class LeapArray<T> {
    
    /**
     * 计算当前时间对应的采样窗口
     * 算法核心:通过取模运算定位循环数组下标
     */
    public WindowWrap<T> currentWindow(long timeMillis) {
        // 1. 计算当前时间属于哪个时间窗口
        long timeId = timeMillis / windowLengthInMs;
        // 2. 计算在循环数组中的索引(取模)
        int idx = (int) (timeId % array.length());
        // 3. 计算窗口开始时间
        long windowStart = timeMillis - timeMillis % windowLengthInMs;
        
        // 4. 获取或创建窗口
        while (true) {
            WindowWrap<T> old = array.get(idx);
            
            if (old == null) {
                // 该槽位为空,创建新窗口(CAS操作保证线程安全)
                WindowWrap<T> window = new WindowWrap<>(windowLengthInMs, windowStart, newEmptyBucket());
                if (array.compareAndSet(idx, null, window)) {
                    return window;
                }
                // CAS失败,说明其他线程已创建,自旋重试
                Thread.yield();
            } else if (windowStart == old.windowStart()) {
                // 窗口未过期,直接返回
                return old;
            } else if (windowStart > old.windowStart()) {
                // 窗口已过期,需要重置(复用对象,避免GC)
                if (updateLock.tryLock()) {
                    try {
                        return resetWindowTo(old, windowStart);
                    } finally {
                        updateLock.unlock();
                    }
                }
                Thread.yield();
            } else {
                // 时钟回拨,理论上不应发生
                return new WindowWrap<>(windowLengthInMs, windowStart, newEmptyBucket());
            }
        }
    }
}

3.2 统计指标实现

java 复制代码
// 指标统计桶(每个窗口内的统计数据)
public class MetricBucket {
    // 使用LongAdder保证高并发下的性能
    private final LongAdder[] counters;
    
    // 统计维度
    public enum MetricEvent {
        PASS,       // 通过请求
        BLOCK,      // 被限流
        EXCEPTION,  // 异常
        SUCCESS,    // 成功
        RT,         // 响应时间
        REQUEST     // 总请求
    }
    
    public void add(MetricEvent event, long count) {
        counters[event.ordinal()].add(count);
    }
    
    public long get(MetricEvent event) {
        return counters[event.ordinal()].sum();
    }
}

3.3 滑动窗口可视化

复制代码
时间轴(假设窗口长度500ms,2个采样窗口构成1秒统计周期):

t=0ms:  [Window1: 0-500ms] [Window2: 500-1000ms]
        │  ▓▓▓ (3请求)      │  ░░░ (0请求)          │
        
t=300ms: 当前请求落在Window1
        │  ▓▓▓▓ (4请求)     │  ░░░                  │
        
t=600ms: 进入Window2,Window1仍有效(在1秒周期内)
        │  ▓▓▓▓             │  ▓ (1请求)            │
        
t=1200ms: Window1过期(超过1秒周期),Window3复用Window1槽位
        │  [Window3: 1000-1500ms]  [Window2: 500-1000ms]  │
        │  ▓ (新请求)              ▓▓ (历史)              │
        
统计当前1秒内总请求 = Window2 + Window3

3.4 并发性能优化

java 复制代码
/**
 * 高并发优化策略:
 * 1. 循环数组避免频繁创建对象(GC友好)
 * 2. LongAdder替代AtomicLong(减少CAS竞争)
 * 3. 锁分段:每个槽位独立,无全局锁
 * 4. 乐观锁CAS操作处理窗口创建/重置
 */
public class SlidingWindowCounter {
    
    // LongAdder vs AtomicLong 性能对比(高并发下)
    // LongAdder: 分散热点,最后求和,吞吐量提升10x+
    private final LongAdder passCount = new LongAdder();
    private final LongAdder blockCount = new LongAdder();
    
    public void incrementPass() {
        passCount.increment();  // 无锁,分散竞争
    }
    
    public long getPassSum() {
        return passCount.sum(); // 最终求和
    }
}

4. 令牌桶算法实战

4.1 令牌桶原理

复制代码
令牌生成速率: 匀速产生(如100个/秒)
令牌桶容量:   最大突发量(如200个)
              │
              ▼
        ┌─────────────┐
        │   令牌桶    │  ◄── 请求来时消耗令牌
        │  [████████] │      有令牌则通过,无则限流
        │  [████████] │      桶满则丢弃新令牌
        │  [░░░░░░░░] │
        └─────────────┘
              │
        令牌产生器(匀速)

4.2 Guava RateLimiter实现

java 复制代码
import com.google.common.util.concurrent.RateLimiter;

@Service
public class GuavaRateLimitService {
    
    // 平滑突发限流:每秒2个,允许突发
    private final RateLimiter smoothBursty = RateLimiter.create(2.0);
    
    // 平滑预热限流:每秒2个,预热期3秒(防止冷启动压垮DB)
    private final RateLimiter smoothWarmingUp = RateLimiter.create(
        2.0, 3, TimeUnit.SECONDS
    );
    
    public void processWithLimit() {
        // 阻塞获取许可
        smoothBursty.acquire();
        doWork();
    }
    
    public boolean tryProcess() {
        // 非阻塞尝试获取
        if (smoothBursty.tryAcquire()) {
            doWork();
            return true;
        }
        return false; // 被限流
    }
}

4.3 分布式令牌桶(Redis + Lua)

java 复制代码
@Component
public class RedisTokenBucket {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String LUA_SCRIPT = 
        "local key = KEYS[1] " +
        "local rate = tonumber(ARGV[1]) " +        // 令牌产生速率(个/秒)
        "local capacity = tonumber(ARGV[2]) " +    // 桶容量
        "local now = tonumber(ARGV[3]) " +         // 当前时间戳(毫秒)
        "local requested = tonumber(ARGV[4]) " +   // 请求令牌数
        
        "local fill_time = capacity / rate " +     // 填满桶需要的时间(秒)
        "local ttl = math.floor(fill_time * 2) " + // 过期时间(2倍填充时间)
        
        // 获取上次更新时间
        "local last_updated = redis.call('hget', key, 'last_updated') " +
        "if last_updated == false then " +
        "  last_updated = 0 " +
        "end " +
        
        // 获取当前令牌数
        "local tokens = redis.call('hget', key, 'tokens') " +
        "if tokens == false then " +
        "  tokens = capacity " +
        "end " +
        
        // 计算新增令牌
        "tokens = math.min(capacity, tokens + (now - last_updated) * rate / 1000) " +
        
        // 判断是否足够
        "local allowed = tokens >= requested " +
        "if allowed then " +
        "  tokens = tokens - requested " +
        "end " +
        
        // 更新Redis
        "redis.call('hset', key, 'tokens', tokens) " +
        "redis.call('hset', key, 'last_updated', now) " +
        "redis.call('expire', key, ttl) " +
        
        "return allowed";
    
    public boolean tryAcquire(String key, int permits, double rate, long capacity) {
        long now = System.currentTimeMillis();
        
        DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
        script.setScriptText(LUA_SCRIPT);
        script.setResultType(Boolean.class);
        
        return redisTemplate.execute(script, 
            Collections.singletonList("token_bucket:" + key),
            String.valueOf(rate),
            String.valueOf(capacity),
            String.valueOf(now),
            String.valueOf(permits)
        );
    }
}

4.4 自定义令牌桶(单机版)

java 复制代码
public class TokenBucket {
    private final long capacity;          // 桶容量
    private final double rate;            // 令牌产生速率(个/毫秒)
    private final AtomicLong tokens;      // 当前令牌数(扩大1000倍避免浮点)
    private volatile long lastTimestamp;  // 上次更新时间
    
    public TokenBucket(long capacity, double permitsPerSecond) {
        this.capacity = capacity * 1000;  // 扩大1000倍
        this.rate = permitsPerSecond;       // 每毫秒产生的令牌数(已扩大)
        this.tokens = new AtomicLong(this.capacity);
        this.lastTimestamp = System.currentTimeMillis();
    }
    
    public synchronized boolean tryAcquire(int permits) {
        long now = System.currentTimeMillis();
        long timeDelta = now - lastTimestamp;
        
        // 计算新增令牌:时间差 * 速率
        long newTokens = (long) (timeDelta * rate);
        long currentTokens = Math.min(capacity, tokens.get() + newTokens);
        
        if (currentTokens >= permits * 1000) {
            tokens.set(currentTokens - permits * 1000);
            lastTimestamp = now;
            return true;
        }
        
        // 更新令牌数(即使不够也要更新,保持匀速)
        tokens.set(currentTokens);
        lastTimestamp = now;
        return false;
    }
}

5. Sentinel架构与源码解析

5.1 Sentinel整体架构

复制代码
┌─────────────────────────────────────────────────────────────┐
│                      应用层(Application)                    │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐   │
│  │  @SentinelResource │  │  Dubbo Adapter │  │  Spring Cloud Gateway │ │
│  │  注解定义资源      │  │  自动埋点      │  │  网关流控            │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│                      核心层(Core)                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐   │
│  │ Slot Chain │  │  Statistic   │  │     Rule Engine     │   │
│  │ 责任链处理  │  │  滑动窗口统计 │  │    规则匹配引擎      │   │
│  │             │  │             │  │                     │   │
│  │ • NodeSelectorSlot    │  │  LeapArray   │  │ • FlowRule         │   │
│  │ • ClusterBuilderSlot   │  │  Bucket      │  │ • DegradeRule      │   │
│  │ • StatisticSlot       │  │  Metric      │  │ • SystemRule       │   │
│  │ • AuthoritySlot       │  │             │  │ • ParamFlowRule    │   │
│  │ • SystemSlot          │  │             │  │ • GatewayRule      │   │
│  │ • ParamFlowSlot       │  │             │  │                     │   │
│  │ • FlowSlot            │  │             │  │                     │   │
│  │ • DegradeSlot         │  │             │  │                     │   │
│  └─────────────┘  └─────────────┘  └─────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│                      基础设施层(Infrastructure)               │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐   │
│  │  Transport │  │   Logger    │  │    DataSource       │   │
│  │  通信传输   │  │   日志      │  │    规则持久化        │   │
│  │  (Netty)   │  │  (LogSlot)  │  │  • Nacos            │   │
│  │            │  │             │  │  • Apollo           │   │
│  │            │  │             │  │  • ZooKeeper        │   │
│  │            │  │             │  │  • Redis            │   │
│  └─────────────┘  └─────────────┘  └─────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

5.2 Slot责任链详解

java 复制代码
// 默认Slot链构建
public class DefaultSlotChainBuilder implements SlotChainBuilder {
    @Override
    public ProcessorChain build() {
        ProcessorChain chain = new DefaultProcessorChain();
        
        // 1. 构建调用树(资源名处理)
        chain.addLast(new NodeSelectorSlot());
        
        // 2. 集群节点构建(处理集群流控)
        chain.addLast(new ClusterBuilderSlot());
        
        // 3. 统计Slot(核心:滑动窗口统计)
        chain.addLast(new StatisticSlot());
        
        // 4. 鉴权Slot(黑白名单)
        chain.addLast(new AuthoritySlot());
        
        // 5. 系统保护Slot(Load/CPU/RT)
        chain.addLast(new SystemSlot());
        
        // 6. 热点参数限流
        chain.addLast(new ParamFlowSlot());
        
        // 7. 流控Slot(QPS/并发线程数)
        chain.addLast(new FlowSlot());
        
        // 8. 降级Slot(熔断降级)
        chain.addLast(new DegradeSlot());
        
        return chain;
    }
}

5.3 流控规则与效果

java 复制代码
// 流控规则配置
FlowRule rule = new FlowRule();
rule.setResource("orderQuery");           // 资源名
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // QPS限流
rule.setCount(1000);                      // 阈值1000 QPS
rule.setStrategy(RuleConstant.STRATEGY_DIRECT); // 直接拒绝
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP); // 预热模式
rule.setWarmUpPeriodSec(10);              // 预热10秒
rule.setMaxQueueingTimeMs(500);           // 匀速排队等待时间

// 控制效果(Control Behavior)
public enum ControlBehavior {
    DEFAULT,        // 直接拒绝(默认)
    WARM_UP,        // 冷启动/预热(令牌桶思想)
    RATE_LIMITER,   // 匀速排队(漏桶思想)
    WARM_UP_RATE_LIMITER // 预热+匀速排队
}

5.4 预热算法实现(冷启动)

java 复制代码
// WarmUpController:基于Guava SmoothWarmingUp思想
public class WarmUpController implements TrafficShapingController {
    
    // 阈值(稳定时)
    protected double count;
    // 冷启动因子(默认3)
    protected int coldFactor;
    // 警告令牌阈值(曲线拐点)
    protected double warningToken;
    // 最大令牌数(桶容量)
    protected double maxToken;
    // 斜率(控制曲线陡峭程度)
    protected double slope;
    
    // 存储的令牌数(扩大1000倍避免浮点)
    protected long storedTokens = 0;
    // 上次填充时间
    protected long lastFilledTime = 0;
    
    @Override
    public boolean canPass(Node node, int acquireCount, boolean prioritized) {
        long currentTime = TimeUtil.currentTimeMillis();
        long passQps = (long) node.passQps();
        
        // 填充令牌(时间差计算)
        long timeDelta = currentTime - lastFilledTime;
        long expectToken = Math.round(timeDelta * count / 1000);
        
        // 计算当前可用令牌
        long oldToken = storedTokens;
        long newToken = Math.min(expectToken + oldToken, maxToken);
        
        // 判断是否超过警告线(冷启动阶段)
        long restToken = newToken;
        if (restToken >= warningToken) {
            // 超过警告线,处于冷启动阶段,计算斜率限制
            long aboveToken = restToken - warningToken;
            // 警告区间内的令牌,消耗速度受斜率限制(慢)
            double warningQps = Math.nextUp(1.0 / (aboveToken * slope + 1.0 / count));
            if (passQps + acquireCount <= warningQps) {
                return true;
            }
        } else {
            // 低于警告线,正常速度
            if (passQps + acquireCount <= count) {
                return true;
            }
        }
        return false;
    }
}

预热曲线可视化

复制代码
QPS
│
count │                    _______________ 稳定期
│                 _____/
│            _____/
│       _____/  斜率控制,逐步放开
│  _____/
│_/
└───────────────────────────────────────> 时间
   0s        10s(预热期)
   
   实际流量:▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░  无预热(直接打满)
            ▓░░░░▓▓░░░░▓▓▓░░░▓▓▓▓░░▓▓▓▓▓▓  预热模式(逐步提升)

6. 企业级降级策略

6.1 降级层次设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                     降级策略金字塔                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  L1: 页面降级    ┌─────────┐   静态化/缓存/关闭非核心模块    │
│  (用户体验层)    │  开关    │   返回兜底页面                 │
│                 └─────────┘                                 │
│                      │                                      │
│  L2: 服务降级    ┌─────────┐   返回默认值/空值/简化数据       │
│  (业务逻辑层)    │  熔断    │   跳过非核心步骤               │
│                 └─────────┘                                 │
│                      │                                      │
│  L3: 数据降级    ┌─────────┐   读缓存/读副本/返回静态数据     │
│  (数据访问层)    │  兜底    │   数据库故障时Mock数据         │
│                 └─────────┘                                 │
│                      │                                      │
│  L4: 基础设施降级 ┌─────────┐   关闭日志/降级DB连接池          │
│  (系统资源层)    │  保护    │   降低线程数/限流更严格        │
│                 └─────────┘                                 │
└─────────────────────────────────────────────────────────────┘

6.2 Sentinel熔断降级实现

java 复制代码
// 熔断降级规则
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource("paymentService");
degradeRule.setGrade(CircuitBreakerStrategy.ERROR_RATIO); // 错误率模式
degradeRule.setCount(0.5);                              // 错误率50%
degradeRule.setTimeWindow(30);                          // 熔断30秒
degradeRule.setMinRequestAmount(10);                    // 最小请求数10
degradeRule.setStatIntervalMs(1000);                    // 统计时长1秒
degradeRule.setSlowRatioThreshold(0.8);               // 慢调用比例阈值

// 熔断状态机
public enum CircuitBreakerState {
    CLOSED,      // 关闭:正常放行
    OPEN,        // 打开:快速失败
    HALF_OPEN    // 半开:探测恢复
}

// 熔断器实现
public class ExceptionCircuitBreaker extends AbstractCircuitBreaker {
    
    @Override
    public void onRequestComplete(Context context) {
        // 获取当前统计
        Snapshot snapshot = statistics.getSnapshot();
        double errorRatio = snapshot.getErrorRatio();
        
        if (state.get() == State.CLOSED) {
            // 关闭状态,判断是否触发熔断
            if (errorRatio > threshold) {
                // 尝试打开熔断器
                if (state.compareAndSet(State.CLOSED, State.OPEN)) {
                    // 调度半开探测任务
                    scheduleHalfOpenTask();
                }
            }
        } else if (state.get() == State.HALF_OPEN) {
            // 半开状态,根据探测结果切换
            if (context.getResult() == Result.ERROR) {
                state.set(State.OPEN); // 失败,重新打开
            } else {
                state.set(State.CLOSED); // 成功,关闭熔断
            }
        }
        // OPEN状态直接拒绝,由前置逻辑处理
    }
}

6.3 业务降级示例

java 复制代码
@Service
public class OrderService {
    
    @Autowired
    private InventoryClient inventoryClient;
    
    @Autowired
    private CacheManager cacheManager;
    
    /**
     * 查询库存(带多级降级)
     */
    @SentinelResource(
        value = "queryInventory",
        blockHandler = "queryInventoryBlockHandler",      // 限流降级
        fallback = "queryInventoryFallback"             // 异常降级
    )
    public Inventory queryInventory(Long skuId) {
        // 1. 尝试调用库存服务
        return inventoryClient.getStock(skuId);
    }
    
    // 限流/熔断时的处理
    public Inventory queryInventoryBlockHandler(Long skuId, BlockException ex) {
        // L2降级:读本地缓存
        Inventory cached = cacheManager.get("inventory:" + skuId);
        if (cached != null) {
            return cached;
        }
        
        // L3降级:返回默认值(允许超卖,后续人工介入)
        Inventory defaultStock = new Inventory();
        defaultStock.setSkuId(skuId);
        defaultStock.setStock(10); // 保守估计
        defaultStock.setSource("DEGRADE_DEFAULT");
        return defaultStock;
    }
    
    // 异常时的处理
    public Inventory queryInventoryFallback(Long skuId, Throwable ex) {
        log.error("Inventory service error, skuId={}", skuId, ex);
        // 返回空库存,前端显示"暂时缺货"
        return Inventory.empty(skuId);
    }
    
    /**
     * 提交订单(流程降级)
     */
    public Order submitOrder(OrderRequest request) {
        // 核心步骤:创建订单(不可降级)
        Order order = createOrder(request);
        
        // 非核心步骤1:扣减库存(可降级:异步补偿)
        try {
            deductInventory(order);
        } catch (Exception e) {
            // 降级:记录待补偿任务,后续MQ补偿
            compensationService.recordInventoryTask(order);
        }
        
        // 非核心步骤2:发送通知(可降级:延迟发送)
        try {
            sendNotification(order);
        } catch (Exception e) {
            // 降级:放入延迟队列
            delayedQueue.enqueue(order);
        }
        
        return order;
    }
}

7. 生产环境最佳实践

7.1 规则动态配置(Nacos集成)

java 复制代码
@Configuration
public class SentinelNacosConfig {
    
    @Bean
    public DataSource nacosFlowRuleDataSource() {
        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = 
            new NacosDataSource<>(
                remoteAddress,
                groupId,
                "flow-rules.json",  // 配置ID
                source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {})
            );
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
        
        // 监听配置变更
        flowRuleDataSource.getProperty().addListener((event, value) -> {
            log.info("Flow rules updated: {}", value);
        });
        
        return flowRuleDataSource;
    }
}

Nacos配置示例

json 复制代码
[
  {
    "resource": "hotProduct",
    "limitApp": "default",
    "grade": 1,
    "count": 1000,
    "strategy": 0,
    "controlBehavior": 0,
    "clusterMode": false,
    "warmUpPeriodSec": 10
  },
  {
    "resource": "payment",
    "limitApp": "default",
    "grade": 0,
    "count": 50,
    "strategy": 0,
    "controlBehavior": 2,
    "maxQueueingTimeMs": 500
  }
]

7.2 监控与告警

java 复制代码
@Component
public class SentinelMetricsExporter {
    
    @Scheduled(fixedRate = 60000)
    public void exportMetrics() {
        // 获取所有资源统计
        for (Map.Entry<String, DefaultNode> entry : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
            String resource = entry.getKey();
            DefaultNode node = entry.getValue();
            
            // 上报Prometheus
            Metrics.gauge("sentinel_pass_qps", 
                Tags.of("resource", resource), 
                node.passQps());
            Metrics.gauge("sentinel_block_qps", 
                Tags.of("resource", resource), 
                node.blockQps());
            Metrics.gauge("sentinel_avg_rt", 
                Tags.of("resource", resource), 
                node.avgRt());
                
            // 异常高时告警
            if (node.exceptionQps() > 10) {
                alertService.send("资源[" + resource + "]异常率过高: " 
                    + node.exceptionQps() + "/s");
            }
        }
    }
}

7.3 全链路防护配置

yaml 复制代码
# application.yml
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
      datasource:
        flow:
          nacos:
            server-addr: ${nacos.server-addr}
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow
        degrade:
          nacos:
            server-addr: ${nacos.server-addr}
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            rule-type: degrade
      # 开启URL自动收敛(RESTful接口合并统计)
      web-context-unify: true
      # 过滤器配置
      filter:
        enabled: true
        url-patterns: /**
      # 饥饿加载
      eager: true

# 网关流控(Spring Cloud Gateway)
spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - name: SentinelGatewayFilter
              args:
                # 自定义API分组
                resource-mode: 0  # 0=routeId, 1=自定义API

7.4 性能优化 checklist

优化项 说明 效果
异步统计 启用sentinel.statistic.max.rt=4900 减少RT计算开销
采样统计 设置csp.sentinel.sample.count=2 降低内存占用
集群流控 Token Server集中管控 避免单机阈值不准
热点参数 针对高频参数(如用户ID、商品ID) 细粒度防护
Sidecar模式 Envoy/Istio集成 无侵入防护

总结

维度 滑动窗口 令牌桶
精准度 高(细粒度时间分片) 中(允许突发)
内存占用 较高(需存储多个窗口) 低(仅维护令牌数)
突发流量 严格平滑 允许突发(桶容量内)
适用场景 实时监控、精准限流 网关入口、匀速处理
Sentinel实现 StatisticSlot核心 WarmUpController

选型建议

  • 入口网关:令牌桶(允许合理突发,保护后端)
  • 服务内部:滑动窗口(精准统计,细粒度控制)
  • 冷启动场景:令牌桶预热模式(WarmUp)
  • 匀速处理:漏桶/排队等待(严格匀速输出)

核心原则:限流是手段,保障业务可用性是目的。限流策略需与降级、熔断、监控形成完整防护体系。


参考文档

相关推荐
兩尛1 小时前
648. 单词替换
算法
MicroTech20252 小时前
MLGO微算法科技利用开放量子系统,Lindbladian 模拟驱动的新一代量子微分方程算法亮相
科技·算法·量子计算
无限进步_2 小时前
138. 随机链表的复制 - 题解与详细分析
c语言·开发语言·数据结构·算法·链表·github·visual studio
烟花落o2 小时前
【数据结构系列04】随机链表的复制、环形链表I、环形链表||
数据结构·算法·leetcode·链表
senijusene2 小时前
Linux软件编程: 线程属性与线程间通信详解
java·linux·jvm·算法
weiabc2 小时前
cout << fixed << setprecision(2) << v; fixed 为什么不用括号,它是函数吗
开发语言·c++·算法
m0_531237172 小时前
C语言-内存函数
c语言·开发语言·算法
独自破碎E2 小时前
【DFS】BISHI77数水坑
算法·深度优先