稳定性保障:限流降级深度解析 ------ Sentinel滑动窗口算法与令牌桶实现
目录
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)
- 匀速处理:漏桶/排队等待(严格匀速输出)
核心原则:限流是手段,保障业务可用性是目的。限流策略需与降级、熔断、监控形成完整防护体系。
参考文档: