Sentinel核心类解析:滑动窗口统计引擎
WindowWrap、LeapArray、ArrayMetric是 Sentinel(阿里巴巴开源的流量治理组件) 中用于实时指标统计的核心模块。这些类共同构成了 Sentinel 的“滑动窗口统计引擎”,为限流、熔断、系统保护等核心功能提供数据支撑。
下面我将从 作用、职责、协作关系 三个维度,帮你彻底理解这些类在 Sentinel 中的意义。
🧩 一、整体架构概览
+------------------+
| ArrayMetric | ← 对外暴露的统一指标接口(Metric)
+------------------+
↓
+------------------+ +----------------------+
| LeapArray<T> | ◄─── | BucketLeapArray / OccupiableBucketLeapArray |
+------------------+ +----------------------+
↓
+------------------+ +------------------+
| WindowWrap<T> | ◄─── | MetricBucket |
+------------------+ +------------------+
WindowWrap<T>:时间窗口的包装器(包含起始时间 + 数据)MetricBucket:具体统计数据容器(pass/block/rt 等)LeapArray<T>:滑动窗口数组(环形 buffer + 时间淘汰逻辑)BucketLeapArray:LeapArray<MetricBucket>的具体实现ArrayMetric:对外提供的 Metric 接口实现,封装了统计操作
💡 一句话总结:
ArrayMetric是用户(如 FlowSlot)使用的“统计 API”;
它内部用LeapArray管理多个WindowWrap<MetricBucket>,每个 bucket 记录一小段时间内的请求指标。
📦 二、逐个类详解
1. WindowWrap<T>
作用:代表一个固定时间长度的统计窗口(bucket)
-
字段:
windowStart: 窗口开始时间(如 1000ms)windowLengthInMs: 窗口长度(如 100ms)value: 实际统计数据(如MetricBucket)
-
关键方法:
isTimeInWindow(time): 判断某时间是否属于此窗口resetTo(startTime): 重置窗口起始时间(用于复用旧 bucket)
✅ 设计意义:解耦“时间信息”和“统计数据”,便于复用和滑动。
2. MetricBucket
作用:具体的指标计数器,记录各类事件数量
-
内部结构:
private final LongAdder[] counters; // 按 MetricEvent 类型索引 private volatile long minRt; // 最小响应时间 -
支持的事件类型(
MetricEvent):PASS:通过的请求BLOCK:被限流拒绝的请求EXCEPTION:异常请求数RT:总响应时间(用于算平均 RT)SUCCESS:成功完成的请求OCCUPIED_PASS:预占(排队等待)的请求(用于“匀速排队”模式)
-
线程安全:使用
LongAdder(高性能并发计数器)
✅ 设计意义:一个 bucket 就是一个“迷你仪表盘”,记录 100ms 内所有关键指标。
3. LeapArray<T>
作用:滑动窗口的核心容器,管理多个
WindowWrap<T>
-
核心机制:
- 环形数组(
AtomicReferenceArray<WindowWrap<T>>) - 时间对齐:
windowStart = time - time % windowLength - 按需创建/重置 bucket(CAS + 低频锁)
- 自动淘汰过期 bucket(
isWindowDeprecated())
- 环形数组(
-
抽象方法:
newEmptyBucket(): 创建空数据resetWindowTo(): 重置过期 bucket
✅ 设计意义:高效、并发安全地维护一个“滚动时间窗口”,避免内存爆炸。
4. BucketLeapArray(继承 LeapArray<MetricBucket>)
作用:为 Sentinel 提供标准的 QPS 统计能力
- 实现了抽象方法:
newEmptyBucket()→new MetricBucket()resetWindowTo()→ 重置时间 + 清零计数器
✅ 这是 Sentinel 默认使用的滑动窗口实现。
🔸 补充:还有
OccupiableBucketLeapArray(支持“请求排队”场景),但你贴的是基础版。
5. ArrayMetric implements Metric
作用:对外暴露的统计接口,业务层(如
StatisticSlot)直接使用它
-
核心能力:
- 写入:
addPass(),addBlock(),addRT()→ 更新当前 bucket - 读取:
pass(): 返回最近intervalInMs内总通过数block(): 总拒绝数rt(): 总响应时间minRt(): 最小响应时间getAvg(MetricEvent.PASS): 平均 QPS(= 总 pass / 时间秒数)
- 写入:
-
内部委托:
- 所有操作都通过
data.currentWindow()获取当前 bucket - 聚合统计时调用
data.values()获取所有有效 bucket
- 所有操作都通过
✅ 设计意义:隐藏底层滑动窗口细节,提供简洁、统一的指标 API。
🔗 三、它们如何协作?—— 以一次请求为例
假设一个请求通过了 Sentinel 的校验(未被限流):
-
StatisticSlot调用:metric.addPass(1); metric.addRT(requestRt); -
ArrayMetric.addPass():- 调用
data.currentWindow()→ 获取当前时间对应的WindowWrap<MetricBucket> - 调用
wrap.value().addPass(1)
- 调用
-
LeapArray.currentWindow():- 计算当前时间属于哪个 bucket(索引 + 起始时间)
- 如果 bucket 不存在 → CAS 创建
- 如果 bucket 过期 → 加锁重置
- 返回有效 bucket
-
MetricBucket.addPass():counters[PASS.ordinal()].add(1)→ 原子递增
-
后续限流判断(如 QPS > 阈值):
- 调用
metric.pass()→ 遍历所有有效 bucket,累加pass()值 - 得到最近 1 秒总通过数 → 与阈值比较
- 调用
🎯 四、为什么这样设计?
| 设计点 | 目的 |
|---|---|
| 滑动窗口 + 环形数组 | 高效利用内存,避免频繁 GC |
| 按时间对齐 bucket | 精确控制统计粒度(如 100ms) |
| CAS + 低频锁 | 高并发下性能优异(写多读少场景) |
| LongAdder | 比 AtomicLong 更高的并发写性能 |
| 抽象 LeapArray | 支持不同统计策略(如普通 bucket / 可占用 bucket) |
| Metric 接口封装 | 解耦统计逻辑与业务逻辑 |
🧠 五、总结:它们在 Sentinel 中的角色
| 类 | 角色 | 类比 |
|---|---|---|
WindowWrap<T> | 时间窗口的“信封” | 信封(写明时间段) |
MetricBucket | 指标的“记分牌” | 记分牌(pass=5, block=2) |
LeapArray<T> | 滑动窗口的“转盘” | 转盘(10 个格子循环使用) |
BucketLeapArray | 标准转盘实现 | 标准记分转盘 |
ArrayMetric | 对外的“统计员” | 统计员(你问他“最近1秒多少QPS?”,他去转盘上加总) |
✅ 最终目标:
在高并发下,以极低开销,实时、准确地回答:“过去 N 秒内,这个接口的 QPS/错误率/RT 是多少?”
这就是 Sentinel 实现智能限流、熔断的基础!
如果你继续深入 Sentinel,会发现:
- 每个
Resource(资源)都有自己的ArrayMetric FlowRuleChecker依赖metric.pass()做 QPS 限流DegradeSlot依赖metric.rt()和exception()做熔断
这些类虽小,却是 Sentinel 实时感知系统状态的“眼睛”和“大脑”。
更多推荐


所有评论(0)