文章目录
- [Sentinel 流控原理深度解析:从SlotChain到热点参数限流的设计哲学](#Sentinel 流控原理深度解析:从SlotChain到热点参数限流的设计哲学)
-
- [📊 目录](#📊 目录)
- [🎯 一、Sentinel设计哲学:从被动防御到主动治理](#🎯 一、Sentinel设计哲学:从被动防御到主动治理)
-
- [💡 Sentinel的核心设计思想](#💡 Sentinel的核心设计思想)
- [🔗 二、SlotChain:可插拔的责任链架构](#🔗 二、SlotChain:可插拔的责任链架构)
-
- [💡 SlotChain的设计哲学](#💡 SlotChain的设计哲学)
- [🔧 SlotChain实现深度解析](#🔧 SlotChain实现深度解析)
- [⚖️ 三、QPS vs 并发数:两种限流模式深度对比](#⚖️ 三、QPS vs 并发数:两种限流模式深度对比)
-
- [💡 两种限流模式的本质差异](#💡 两种限流模式的本质差异)
- [🔥 四、热点参数限流:精准控制的智慧](#🔥 四、热点参数限流:精准控制的智慧)
-
- [💡 热点参数限流的设计思想](#💡 热点参数限流的设计思想)
- [🔧 热点参数限流实现深度解析](#🔧 热点参数限流实现深度解析)
Sentinel 流控原理深度解析:从SlotChain到热点参数限流的设计哲学
📊 目录
- 🎯 一、Sentinel设计哲学:从被动防御到主动治理
- 🔗 二、SlotChain:可插拔的责任链架构
- ⚖️ 三、QPS vs 并发数:两种限流模式深度对比
- 🔥 四、热点参数限流:精准控制的智慧
- 📈 五、滑动窗口算法:时间窗口的魔法
- 🎪 六、集群流控:分布式一致性挑战
- 🔧 七、生产实践:配置、监控与调优
🎯 一、Sentinel设计哲学:从被动防御到主动治理
💡 Sentinel的核心设计思想
Sentinel与传统限流框架的本质区别:
java
/**
* Sentinel设计哲学解析
* 从防御到治理的演进
*/
@Component
@Slf4j
public class SentinelPhilosophy {
/**
* 流量治理的四个维度
*/
public enum TrafficGovernanceDimension {
/**
* 维度一:流量控制
* 哲学:预防胜于治疗
*/
FLOW_CONTROL(
"流量控制",
"预防系统过载,保护系统稳定性",
"""
设计思想:
- 在入口处控制流量
- 避免突发流量击垮系统
- 平滑流量,削峰填谷
""",
"""
实现方式:
- QPS限流
- 并发数限流
- 匀速排队
- 冷启动
"""
),
/**
* 维度二:熔断降级
* 哲学:快速失败,避免雪崩
*/
CIRCUIT_BREAKER(
"熔断降级",
"保护系统免受过载和故障影响",
"""
设计思想:
- 监控依赖服务的健康状态
- 异常时快速失败
- 避免资源耗尽导致雪崩
""",
"""
实现方式:
- 慢调用比例熔断
- 异常比例熔断
- 异常数熔断
- 自适应恢复
"""
),
/**
* 维度三:系统保护
* 哲学:自我保护,确保存活
*/
SYSTEM_PROTECT(
"系统保护",
"保护系统自身,防止被拖垮",
"""
设计思想:
- 监控系统自身指标
- 根据系统负载动态限流
- 保证系统不崩溃
""",
"""
实现方式:
- LOAD自适应保护
- CPU使用率保护
- 平均RT保护
- 线程数保护
"""
),
/**
* 维度四:热点参数
* 哲学:精准控制,资源优化
*/
HOT_PARAM(
"热点参数",
"对热点资源进行精准控制",
"""
设计思想:
- 识别热点参数
- 针对性限流
- 优化资源分配
""",
"""
实现方式:
- 参数统计
- 热点识别
- 差异化限流
"""
);
private final String name;
private final String purpose;
private final String philosophy;
private final String implementation;
}
/**
* Sentinel与传统限流的对比
*/
@Data
@Builder
public static class Comparison {
// 传统限流的问题
public static class TraditionalLimiterProblems {
/**
* 1. 静态配置
*/
public static final String STATIC_CONFIG = """
// 传统限流:硬编码配置
public class TraditionalRateLimiter {
private static final int QPS_LIMIT = 100; // 硬编码
public boolean tryAcquire() {
// 固定限流值
return counter < QPS_LIMIT;
}
}
问题:
- 无法动态调整
- 难以适应变化流量
- 每个服务需要单独配置
""";
/**
* 2. 缺乏维度
*/
public static final String LACK_DIMENSIONS = """
// 传统限流:单一维度
public boolean allowRequest() {
// 只有QPS一个维度
return getCurrentQps() < MAX_QPS;
}
问题:
- 无法区分调用来源
- 无法识别热点数据
- 无法进行精准控制
""";
/**
* 3. 被动防御
*/
public static final String PASSIVE_DEFENSE = """
// 传统限流:被动响应
public Response handleRequest(Request req) {
if (!rateLimiter.tryAcquire()) {
return Response.error("Rate limited"); // 被动拒绝
}
return process(req);
}
问题:
- 只有拒绝,没有治理
- 无法自适应调整
- 无法学习流量模式
""";
}
// Sentinel的解决方案
public static class SentinelSolutions {
/**
* 1. 动态规则
*/
public static final String DYNAMIC_RULES = """
// Sentinel:动态规则配置
@Configuration
public class SentinelConfig {
@PostConstruct
public void initRules() {
// 从配置中心加载规则
List<FlowRule> rules = loadRulesFromNacos();
// 动态更新规则
FlowRuleManager.loadRules(rules);
// 监听规则变化
registerRuleChangeListener();
}
// 运行时调整规则
public void adjustRule(String resource, int newQps) {
List<FlowRule> rules = FlowRuleManager.getRules();
rules.stream()
.filter(r -> r.getResource().equals(resource))
.forEach(r -> r.setCount(newQps));
FlowRuleManager.loadRules(rules);
}
}
优势:
- 规则动态生效
- 无需重启服务
- 支持灰度发布
""";
/**
* 2. 多维度控制
*/
public static final String MULTI_DIMENSION = """
// Sentinel:多维度流量控制
public class MultiDimensionControl {
// 1. 调用方维度
@SentinelResource(
value = "getUserInfo",
blockHandler = "userBlockHandler",
blockHandlerClass = UserBlockHandler.class
)
public User getUser(String userId, String source) {
// 根据调用方(source)进行不同限流
return userService.getUser(userId);
}
// 2. 热点参数维度
@SentinelResource(
value = "queryProduct",
blockHandler = "hotParamBlockHandler"
)
public Product queryProduct(
@RequestParam Long productId,
@RequestParam String userId) {
// 对productId进行热点限流
return productService.getProduct(productId);
}
// 3. 系统维度
@PostConstruct
public void initSystemRule() {
SystemRule rule = new SystemRule();
rule.setHighestSystemLoad(4.0); // 系统Load保护
rule.setAvgRt(100); // 平均RT保护
rule.setMaxThread(500); // 线程数保护
rule.setQps(1000); // 入口QPS保护
SystemRuleManager.loadRules(Collections.singletonList(rule));
}
}
优势:
- 精细化控制
- 资源优化分配
- 防止热点问题
""";
/**
* 3. 主动治理
*/
public static final String ACTIVE_GOVERNANCE = """
// Sentinel:主动流量治理
@Component
public class ActiveTrafficGovernance {
// 1. 实时监控
@Scheduled(fixedDelay = 5000)
public void monitorAndAdjust() {
// 获取实时指标
ClusterNode node = ClusterBuilderSlot.getClusterNode("resource");
long passQps = node.passQps();
long blockQps = node.blockQps();
double blockRatio = (double) blockQps / (passQps + blockQps);
// 根据指标自动调整
if (blockRatio > 0.1) { // 阻塞率过高
adjustRule("resource", RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
} else if (blockRatio < 0.01) { // 阻塞率过低
adjustRule("resource", RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
}
}
// 2. 自适应保护
public void adaptiveProtection() {
// 根据系统负载动态调整
double systemLoad = getSystemLoad();
if (systemLoad > 0.8) {
// 负载过高,启用激进限流
enableAggressiveLimiting();
} else {
// 负载正常,恢复正常
enableNormalLimiting();
}
}
// 3. 流量整形
public void trafficShaping() {
// 匀速排队模式
FlowRule rule = new FlowRule("resource");
rule.setCount(100);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
rule.setMaxQueueingTimeMs(500); // 排队时间
FlowRuleManager.loadRules(Collections.singletonList(rule));
}
}
优势:
- 主动调整,而非被动响应
- 自适应流量变化
- 智能化流量治理
""";
}
}
}
🔗 二、SlotChain:可插拔的责任链架构
💡 SlotChain的设计哲学
责任链模式在流量控制中的应用:
Slot执行链核心流程
默认
入口
自定义
通过
拒绝
是
否
否
是
是
否
是
否
请求进入
SlotChain处理器
- NodeSelectorSlot 选择资源节点
默认节点
入口节点
自定义节点 - ClusterBuilderSlot 构建ClusterNode
统计集群指标 - LogSlot 记录请求日志
- StatisticSlot 统计决策
更新通过统计
更新拒绝统计 - SystemSlot 记录拒绝原因
返回拒绝结果
系统保护检查
系统是否过载?
系统保护生效 - AuthoritySlot 授权检查
是否有权限?
权限不足 - FlowSlot 流量控制检查
是否限流?
流量控制生效 - DegradeSlot 熔断降级检查
是否熔断?
熔断生效 - 执行业务逻辑 实际业务调用
- 返回结果
🔧 SlotChain实现深度解析
java
/**
* SlotChain实现深度解析
* 展示Sentinel责任链的核心实现
*/
@Slf4j
public class SlotChainDeepDive {
/**
* SlotChain构建器
*/
public static class DefaultProcessorSlotChain extends ProcessorSlotChain {
// Slot链头节点
private AbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper,
Object param, int count, boolean prioritized, Object... args) throws Throwable {
// 链头,调用下一个Slot
super.fireEntry(context, resourceWrapper, param, count, prioritized, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper,
int count, Object... args) {
super.fireExit(context, resourceWrapper, count, args);
}
};
// 当前Slot指针
private AbstractLinkedProcessorSlot<?> end = first;
/**
* 添加Slot到链尾
*/
@Override
public void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {
end.setNext(protocolProcessor);
end = protocolProcessor;
}
/**
* 执行entry方法(请求进入)
*/
@Override
public void entry(Context context, ResourceWrapper resourceWrapper,
Object param, int count, boolean prioritized, Object... args) throws Throwable {
first.transformEntry(context, resourceWrapper, param, count, prioritized, args);
}
/**
* 执行exit方法(请求退出)
*/
@Override
public void exit(Context context, ResourceWrapper resourceWrapper,
int count, Object... args) {
first.exit(context, resourceWrapper, count, args);
}
}
/**
* NodeSelectorSlot:节点选择器
* 负责创建调用链路节点
*/
public static class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper,
Object param, int count, boolean prioritized, Object... args) throws Throwable {
// 1. 获取或创建DefaultNode
DefaultNode node = context.getCurNode();
if (node == null) {
// 创建当前资源的节点
node = new DefaultNode(resourceWrapper, null);
context.setCurNode(node);
}
// 2. 构建调用链
if (context.getCurEntry().getCurNode() == null) {
context.getCurEntry().setCurNode(node);
}
// 3. 构建调用关系
DefaultNode selectedNode = null;
if (context.getOrigin() != null) {
// 按调用方构建节点树
String origin = context.getOrigin();
selectedNode = context.getOrCreateOriginNode(origin);
selectedNode.addChild(node);
}
// 4. 调用下一个Slot
fireEntry(context, resourceWrapper, param, count, prioritized, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper,
int count, Object... args) {
// 清理上下文
if (context.getCurEntry() != null) {
context.getCurEntry().setCurNode(null);
}
fireExit(context, resourceWrapper, count, args);
}
}
/**
* StatisticSlot:统计Slot
* 核心的统计逻辑实现
*/
public static class StatisticSlot extends AbstractLinkedProcessorSlot<Object> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper,
Object param, int count, boolean prioritized, Object... args) throws Throwable {
try {
// 1. 执行后续Slot
fireEntry(context, resourceWrapper, param, count, prioritized, args);
// 2. 请求通过,更新统计
for (ProcessorSlotEntryCallback<Object> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onPass(context, resourceWrapper, param, count, args);
}
// 3. 更新通过计数
recordPass(resourceWrapper, count, args);
} catch (BlockException e) {
// 4. 请求被限流/降级
for (ProcessorSlotEntryCallback<Object> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onBlocked(e, context, resourceWrapper, param, count, args);
}
// 5. 更新阻塞计数
recordBlock(resourceWrapper, count, args, e);
throw e;
} catch (Throwable e) {
// 6. 业务异常
for (ProcessorSlotEntryCallback<Object> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onError(context, resourceWrapper, e, param, count, args);
}
// 7. 更新异常计数
recordError(resourceWrapper, count, args, e);
throw e;
}
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper,
int count, Object... args) {
// 1. 获取响应时间
long rt = TimeUtil.currentTimeMillis() - context.getCurEntry().getCreateTime();
// 2. 更新完成计数
recordComplete(resourceWrapper, count, args, rt);
// 3. 调用完成回调
for (ProcessorSlotExitCallback handler : StatisticSlotCallbackRegistry.getExitCallbacks()) {
handler.onExit(context, resourceWrapper, count, args);
}
// 4. 执行后续Slot的exit
fireExit(context, resourceWrapper, count, args);
}
/**
* 记录通过计数
*/
private void recordPass(ResourceWrapper resourceWrapper, int count, Object... args) {
// 更新各个维度的统计
ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceWrapper.getName());
if (clusterNode != null) {
clusterNode.addPassRequest(count);
}
DefaultNode defaultNode = (DefaultNode) CtSph.getContext().getCurNode();
if (defaultNode != null) {
defaultNode.addPassRequest(count);
}
String origin = CtSph.getContext().getOrigin();
if (StringUtil.isNotBlank(origin)) {
// 按调用方统计
StatisticNode originNode = ClusterBuilderSlot.getOriginNode(origin, resourceWrapper.getName());
if (originNode != null) {
originNode.addPassRequest(count);
}
}
// 全局统计
Constants.ENTRY_NODE.increaseThreadNum();
Constants.ENTRY_NODE.addPassRequest(count);
}
/**
* 记录完成统计
*/
private void recordComplete(ResourceWrapper resourceWrapper, int count,
Object... args, long rt) {
// 更新响应时间统计
ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceWrapper.getName());
if (clusterNode != null) {
clusterNode.addRt(rt);
clusterNode.addSuccessRequest(count);
}
// 减少线程数
Constants.ENTRY_NODE.decreaseThreadNum();
}
}
/**
* FlowSlot:流量控制Slot
*/
public static class FlowSlot extends AbstractLinkedProcessorSlot<Object> {
private final FlowRuleChecker checker = new FlowRuleChecker();
@Override
public void entry(Context context, ResourceWrapper resourceWrapper,
Object param, int count, boolean prioritized, Object... args) throws Throwable {
// 1. 检查流控规则
checker.checkFlow(resourceWrapper, context, param, count, prioritized);
// 2. 执行后续Slot
fireEntry(context, resourceWrapper, param, count, prioritized, args);
}
}
/**
* 流量规则检查器
*/
public static class FlowRuleChecker {
public void checkFlow(ResourceWrapper resource, Context context,
Object param, int count, boolean prioritized) throws BlockException {
// 1. 获取资源的流控规则
List<FlowRule> rules = FlowRuleManager.getRulesForResource(resource.getName());
if (rules == null || rules.isEmpty()) {
return; // 无规则,直接通过
}
// 2. 按优先级排序
rules.sort((o1, o2) -> {
int p1 = o1.getStrategy();
int p2 = o2.getStrategy();
return Integer.compare(p1, p2);
});
// 3. 依次检查规则
for (FlowRule rule : rules) {
if (!canPassCheck(rule, context, param, count)) {
// 触发流控
String limitApp = rule.getLimitApp();
throw new FlowException(rule.getLimitApp(), rule);
}
}
}
/**
* 检查单个规则
*/
private boolean canPassCheck(FlowRule rule, Context context,
Object param, int count) {
String limitApp = rule.getLimitApp();
// 检查限流应用
if (!rule.getLimitApp().equals(limitApp) &&
!RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {
return true;
}
// 选择节点进行统计
Node selectedNode = selectNodeByStrategy(rule, context);
if (selectedNode == null) {
return true;
}
// 根据控制行为检查
return rule.getRater().canPass(selectedNode, count, rule);
}
/**
* 根据策略选择节点
*/
private Node selectNodeByStrategy(FlowRule rule, Context context) {
String strategy = rule.getStrategy();
switch (strategy) {
case RuleConstant.STRATEGY_DIRECT:
// 直接限流:针对当前资源
return ClusterBuilderSlot.getClusterNode(rule.getResource());
case RuleConstant.STRATEGY_RELATE:
// 关联限流:针对关联资源
String refResource = rule.getRefResource();
return ClusterBuilderSlot.getClusterNode(refResource);
case RuleConstant.STRATEGY_CHAIN:
// 链路限流:针对入口资源
String entryResource = rule.getRefResource();
if (context.getCurEntry() != null &&
context.getCurEntry().getResourceWrapper().getName().equals(entryResource)) {
return context.getCurNode();
}
return null;
default:
return null;
}
}
}
}
⚖️ 三、QPS vs 并发数:两种限流模式深度对比
💡 两种限流模式的本质差异
QPS与并发数限流的核心区别:
java
/**
* QPS vs 并发数限流深度对比
* 从原理到实现的全面分析
*/
@Component
@Slf4j
public class QpsVsConcurrencyComparison {
/**
* 两种限流模式对比
*/
@Data
@Builder
public static class Comparison {
private final String dimension; // 维度
private final QpsLimiter qps; // QPS限流
private final ConcurrencyLimiter concurrency; // 并发数限流
private final String technicalInsight; // 技术洞察
public static List<Comparison> generate() {
return Arrays.asList(
Comparison.builder()
.dimension("限流目标")
.qps(QpsLimiter.builder()
.target("控制单位时间内的请求数量")
.focus("请求频率控制")
.metric("请求/秒")
.goal("防止系统过载,平滑流量")
.build())
.concurrency(ConcurrencyLimiter.builder()
.target("控制同时处理的请求数量")
.focus("资源使用控制")
.metric("并发线程数")
.goal("防止资源耗尽,保护系统稳定")
.build())
.technicalInsight("""
本质区别:
- QPS限流:控制"速率",关注单位时间内的请求数
- 并发数限流:控制"并发度",关注同时处理的请求数
类比:
- QPS:高速公路的车辆通过速率(辆/小时)
- 并发数:高速公路上的同时行驶车辆数
""")
.build(),
Comparison.builder()
.dimension("实现原理")
.qps(QpsLimiter.builder()
.principle("""
基于时间窗口的计数
1. 固定窗口计数器
2. 滑动窗口计数器
3. 令牌桶算法
4. 漏桶算法
""")
.algorithm("""
// 滑动窗口计数器示例
public class SlidingWindow {
// 窗口分成多个小格子
private AtomicLong[] timeSlots;
private long windowSize; // 窗口大小(ms)
private int slotCount; // 格子数量
public synchronized boolean tryAcquire() {
long now = System.currentTimeMillis();
long windowStart = now - windowSize;
// 清理过期格子
clearExpiredSlots(windowStart);
// 统计窗口内请求数
long count = getCountInWindow(windowStart, now);
if (count < threshold) {
// 记录当前请求
recordRequest(now);
return true;
}
return false;
}
}
""")
.build())
.concurrency(ConcurrencyLimiter.builder()
.principle("""
基于信号量的控制
1. 计数信号量
2. 线程池大小
3. 数据库连接池
""")
.algorithm("""
// 信号量实现示例
public class SemaphoreLimiter {
private final Semaphore semaphore;
private final int maxConcurrency;
public SemaphoreLimiter(int maxConcurrency) {
this.maxConcurrency = maxConcurrency;
this.semaphore = new Semaphore(maxConcurrency);
}
public boolean tryAcquire() {
return semaphore.tryAcquire();
}
public void release() {
semaphore.release();
}
// 获取当前并发数
public int getCurrentConcurrency() {
return maxConcurrency - semaphore.availablePermits();
}
}
// 或使用线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
// 线程池本身就是一个并发限制器
""")
.build())
.technicalInsight("""
实现机制差异:
- QPS限流:需要时间维度统计,实现相对复杂
- 并发数限流:基于资源计数,实现相对简单
性能影响:
- QPS限流:统计开销主要在计数和窗口管理
- 并发数限流:上下文切换和线程管理开销
""")
.build(),
Comparison.builder()
.dimension("适用场景")
.qps(QpsLimiter.builder()
.scenarios("""
1. API限流
- 防止API被过度调用
- 保护后端服务
2. 爬虫控制
- 限制爬虫频率
- 防止数据被抓取
3. 秒杀活动
- 控制秒杀请求频率
- 平滑突发流量
4. 第三方接口调用
- 遵守第三方接口调用限制
- 避免被限流
""")
.advantages("""
1. 精确控制请求频率
2. 平滑流量,避免突发
3. 易于理解和配置
""")
.limitations("""
1. 无法限制长时间运行的任务
2. 对资源占用的控制较弱
3. 突发流量可能被拒绝
""")
.build())
.concurrency(ConcurrencyLimiter.builder()
.scenarios("""
1. 数据库连接保护
- 防止连接池耗尽
- 避免数据库过载
2. 线程池保护
- 控制并发线程数
- 避免线程耗尽
3. 慢查询保护
- 防止慢查询占用所有连接
- 保证系统响应性
4. 资源敏感操作
- 大文件上传/下载
- 复杂计算任务
- 外部服务调用
""")
.advantages("""
1. 直接保护系统资源
2. 防止慢请求堆积
3. 避免资源耗尽
""")
.limitations("""
1. 无法控制请求频率
2. 可能造成请求排队
3. 配置不当可能导致饥饿
""")
.build())
.technicalInsight("""
场景选择原则:
选择QPS限流当:
- 需要控制请求频率
- 防止API被刷
- 遵守外部调用限制
选择并发数限流当:
- 需要保护有限资源
- 有长时间运行的任务
- 防止慢请求影响系统
最佳实践:两者结合使用
- 外层用QPS限流控制频率
- 内层用并发数限流保护资源
""")
.build()
);
}
}
/**
* Sentinel中的QPS限流实现
*/
public static class SentinelQpsLimiter {
/**
* 滑动窗口计数器实现
*/
public class SlidingWindow {
// 窗口格子数组
private final AtomicReferenceArray<WindowWrap> array;
// 窗口大小(毫秒)
private final int windowLengthInMs;
// 采样窗口数量
private final int sampleCount;
// 每个格子的时长
private final int intervalInMs;
/**
* 尝试通过
*/
public boolean tryPass(int acquireCount, double threshold) {
// 获取当前时间窗口
long currentTime = TimeUtil.currentTimeMillis();
long currentWindowStart = calculateWindowStart(currentTime);
int idx = calculateWindowIdx(currentTime);
// 计算窗口开始时间
long oldWindowStart = 0L;
WindowWrap oldWindow = array.get(idx);
if (oldWindow != null) {
oldWindowStart = oldWindow.windowStart();
if (oldWindowStart == currentWindowStart) {
// 当前窗口
long pass = oldWindow.value().addAndGet(acquireCount);
return pass <= threshold;
}
}
// 重置窗口
WindowWrap newWindow = new WindowWrap(intervalInMs, currentWindowStart, new AtomicLong(0));
if (array.compareAndSet(idx, oldWindow, newWindow)) {
long pass = newWindow.value().addAndGet(acquireCount);
return pass <= threshold;
} else {
// 并发情况,重试
Thread.yield();
return tryPass(acquireCount, threshold);
}
}
/**
* 获取窗口内总请求数
*/
public long getWindowPass(long currentTime) {
long sum = 0L;
for (int i = 0; i < array.length(); i++) {
WindowWrap w = array.get(i);
if (w != null && isWindowDeprecated(currentTime, w)) {
sum += w.value().get();
}
}
return sum;
}
/**
* 计算窗口索引
*/
private int calculateWindowIdx(long timeMillis) {
long timeId = timeMillis / intervalInMs;
return (int)(timeId % array.length());
}
/**
* 计算窗口开始时间
*/
private long calculateWindowStart(long timeMillis) {
return timeMillis - timeMillis % intervalInMs;
}
/**
* 判断窗口是否过期
*/
private boolean isWindowDeprecated(long currentTime, WindowWrap windowWrap) {
return currentTime - windowWrap.windowStart() <= windowLengthInMs;
}
}
/**
* 窗口包装类
*/
public static class WindowWrap {
private final long windowLengthInMs;
private long windowStart;
private AtomicLong value;
public WindowWrap(long windowLengthInMs, long windowStart, AtomicLong value) {
this.windowLengthInMs = windowLengthInMs;
this.windowStart = windowStart;
this.value = value;
}
public long windowStart() {
return windowStart;
}
public AtomicLong value() {
return value;
}
}
}
/**
* Sentinel中的并发数限流实现
*/
public static class SentinelConcurrencyLimiter {
/**
* 并发数限流器
*/
public class ConcurrencyLimiter {
// 最大并发数
private final int maxConcurrency;
// 当前并发数
private final AtomicInteger currentConcurrency = new AtomicInteger(0);
// 等待队列
private final Queue<RequestContext> waitingQueue = new ConcurrentLinkedQueue<>();
// 超时时间
private final long timeoutInMs;
/**
* 尝试获取许可
*/
public boolean tryAcquire(RequestContext context) {
long startTime = System.currentTimeMillis();
while (true) {
int current = currentConcurrency.get();
if (current >= maxConcurrency) {
// 并发数已满,检查是否等待
if (context.getTimeoutInMs() <= 0) {
return false; // 不等待,直接失败
}
// 加入等待队列
waitingQueue.offer(context);
// 计算剩余等待时间
long elapsed = System.currentTimeMillis() - startTime;
long remaining = context.getTimeoutInMs() - elapsed;
if (remaining <= 0) {
waitingQueue.remove(context);
return false; // 等待超时
}
// 等待
try {
synchronized (context) {
context.wait(remaining);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
waitingQueue.remove(context);
return false;
}
// 被唤醒,重试
continue;
}
// 尝试增加并发数
if (currentConcurrency.compareAndSet(current, current + 1)) {
context.setAcquired(true);
return true;
}
// CAS失败,重试
Thread.yield();
}
}
/**
* 释放许可
*/
public void release() {
int current = currentConcurrency.decrementAndGet();
// 唤醒等待队列中的请求
if (!waitingQueue.isEmpty() && current < maxConcurrency) {
RequestContext context = waitingQueue.poll();
if (context != null) {
synchronized (context) {
context.notify();
}
}
}
}
}
/**
* 请求上下文
*/
public static class RequestContext {
private final long timeoutInMs;
private volatile boolean acquired = false;
public RequestContext(long timeoutInMs) {
this.timeoutInMs = timeoutInMs;
}
public long getTimeoutInMs() {
return timeoutInMs;
}
public boolean isAcquired() {
return acquired;
}
public void setAcquired(boolean acquired) {
this.acquired = acquired;
}
}
}
/**
* 组合限流策略
*/
public class CombinedLimiter {
// QPS限流器
private final SentinelQpsLimiter.SlidingWindow qpsLimiter;
// 并发数限流器
private final SentinelConcurrencyLimiter.ConcurrencyLimiter concurrencyLimiter;
public CombinedLimiter(int qpsThreshold, int concurrencyThreshold) {
this.qpsLimiter = new SentinelQpsLimiter.SlidingWindow();
this.concurrencyLimiter = new SentinelConcurrencyLimiter.ConcurrencyLimiter(concurrencyThreshold);
}
/**
* 双重检查
*/
public boolean tryAcquire() {
// 1. 先检查QPS
if (!qpsLimiter.tryPass(1, qpsThreshold)) {
return false; // QPS超限
}
// 2. 再检查并发数
RequestContext context = new RequestContext(100); // 100ms超时
if (!concurrencyLimiter.tryAcquire(context)) {
return false; // 并发数超限
}
return true;
}
/**
* 释放
*/
public void release() {
concurrencyLimiter.release();
}
}
}
🔥 四、热点参数限流:精准控制的智慧
💡 热点参数限流的设计思想
热点参数识别与差异化限流:
热点检测流程
是
否
热点识别算法
滑动计数窗口
Top-N 统计
频次阈值检测
时间衰减因子
热点参数示例
商品ID: 12345
用户ID: 67890
IP地址: 192.168.1.1
请求进入
提取请求参数
参数识别
普通参数
热点参数
普通限流规则
热点参数检测
参数统计器
滑动窗口统计
热点检测算法
是否为热点?
应用热点限流规则
应用普通限流规则
统一限流
差异化限流
通过/拒绝
🔧 热点参数限流实现深度解析
java
/**
* 热点参数限流深度解析
* 包含热点检测、参数统计、差异化限流
*/
@Component
@Slf4j
public class HotParamLimiterDeepDive {
/**
* 热点参数限流规则
*/
@Data
public class HotParamRule {
// 资源名
private String resource;
// 参数索引(0表示第一个参数)
private Integer paramIdx;
// 限流阈值
private double count;
// 统计窗口时长(毫秒)
private long durationInSec = 1;
// 热点参数类型
private Class<?> paramClass;
// 是否启用热点检测
private boolean hotDetectionEnabled = true;
// 热点检测阈值
private int hotDetectionThreshold = 100;
// 热点检测窗口大小
private int hotDetectionWindow = 10;
// 差异化限流规则
private Map<Object, Double> specificRules = new HashMap<>();
/**
* 获取参数的限流阈值
*/
public double getThresholdForParam(Object paramValue) {
// 1. 检查是否有特定规则
if (specificRules.containsKey(paramValue)) {
return specificRules.get(paramValue);
}
// 2. 检查是否为热点参数
if (hotDetectionEnabled && isHotParam(paramValue)) {
// 热点参数使用更严格的限制
return count * 0.5; // 热点参数限流减半
}
// 3. 默认规则
return count;
}
/**
* 检测是否为热点参数
*/
private boolean isHotParam(Object paramValue) {
// 从统计器中获取参数统计
ParamMetric metric = getParamMetric(paramValue);
if (metric == null) {
return false;
}
// 检查是否超过热点阈值
long passQps = metric.passQps();
return passQps > hotDetectionThreshold;
}
/**
* 添加特定参数规则
*/
public void addSpecificRule(Object paramValue, double threshold) {
specificRules.put(paramValue, threshold);
}
}
/**
* 参数统计器
*/
public class ParamMetric {
// 参数值
private final Object paramValue;
// 滑动窗口统计器
private final SlidingWindowCounter counter;
// 最后访问时间
private volatile long lastAccessTime;
// 是否为热点
private volatile boolean isHot = false;
// 热度分数
private volatile double hotScore = 0.0;
public ParamMetric(Object paramValue, int windowLength, int interval) {
this.paramValue = paramValue;
this.counter = new SlidingWindowCounter(windowLength, interval);
this.lastAccessTime = System.currentTimeMillis();
}
/**
* 增加通过计数
*/
public void addPass(int count) {
counter.addPass(count);
lastAccessTime = System.currentTimeMillis();
updateHotScore();
}
/**
* 增加阻塞计数
*/
public void addBlock(int count) {
counter.addBlock(count);
}
/**
* 获取QPS
*/
public long passQps() {
return counter.passQps();
}
/**
* 获取总请求数
*/
public long totalRequests() {
return counter.totalRequests();
}
/**
* 更新热度分数
*/
private void updateHotScore() {
long currentTime = System.currentTimeMillis();
long elapsed = currentTime - lastAccessTime;
// 热度衰减算法
if (elapsed > 1000) { // 1秒衰减
hotScore *= Math.exp(-elapsed / 1000.0); // 指数衰减
}
// 增加当前请求的热度
hotScore += 1.0;
// 判断是否为热点
isHot = hotScore > 100; // 阈值可配置
}
/**
* 获取热度分数
*/
public double getHotScore() {
return hotScore;
}
public boolean isHot() {
return isHot;
}
}
/**
* 热点参数限流控制器
*/
public class HotParamFlowController {
// 参数统计器映射
private final ConcurrentMap<Object, ParamMetric> paramMetrics =
new ConcurrentHashMap<>();
// 规则映射
private final ConcurrentMap<String, HotParamRule> rules =
new ConcurrentHashMap<>();
// 热点检测器
private final HotParamDetector detector;
// 清理任务
private final ScheduledExecutorService cleanupExecutor;
public HotParamFlowController() {
this.detector = new HotParamDetector(this);
this.cleanupExecutor = Executors.newSingleThreadScheduledExecutor(
r -> new Thread(r, "hot-param-cleanup"));
// 定期清理过期参数
cleanupExecutor.scheduleAtFixedRate(
this::cleanupExpiredParams, 1, 1, TimeUnit.MINUTES);
// 启动热点检测
detector.start();
}
/**
* 检查请求是否通过
*/
public boolean canPass(String resource, Object[] args, HotParamRule rule) {
if (args == null || args.length <= rule.getParamIdx()) {
return true; // 参数不足,直接通过
}
Object paramValue = args[rule.getParamIdx()];
if (paramValue == null) {
return true; // 参数为空,直接通过
}
// 获取参数统计器
ParamMetric metric = getOrCreateMetric(paramValue, rule);
// 获取该参数的限流阈值
double threshold = rule.getThresholdForParam(paramValue);
// 检查是否超过阈值
long passQps = metric.passQps();
if (passQps >= threshold) {
// 限流
metric.addBlock(1);
return false;
}
// 通过
metric.addPass(1);
return true;
}
/**
* 获取或创建参数统计器
*/
private ParamMetric getOrCreateMetric(Object paramValue, HotParamRule rule) {
return paramMetrics.computeIfAbsent(paramValue, k ->
new ParamMetric(
paramValue,
(int) (rule.getDurationInSec() * 1000),
1000 // 1秒一个窗口
)
);
}
/**
* 清理过期参数
*/
private void cleanupExpiredParams() {
long now = System.currentTimeMillis();
long expireTime = 5 * 60 * 1000; // 5分钟过期
Iterator<Map.Entry<Object, ParamMetric>> it = paramMetrics.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Object, ParamMetric> entry = it.next();
ParamMetric metric = entry.getValue();
if (now - metric.lastAccessTime > expireTime) {
it.remove();
log.debug("Cleaned up expired param metric: {}", entry.getKey());
}
}
}
/**
* 获取热点参数列表
*/
public List<Map.Entry<Object, Double>> getHotParams(int topN) {
List<Map.Entry<Object, Double>> hotParams = new ArrayList<>();
for (Map.Entry<Object, ParamMetric> entry : paramMetrics.entrySet()) {
if (entry.getValue().isHot()) {
hotParams.add(new AbstractMap.SimpleEntry<>(
entry.getKey(),
entry.getValue().getHotScore()
));
}
}
// 按热度排序
hotParams.sort((a, b) -> Double.compare(b.getValue(), a.getValue()));
// 返回TopN
return hotParams.subList(0, Math.min(topN, hotParams.size()));
}
}
/**
* 热点参数检测器
*/
public class HotParamDetector {
private final HotParamFlowController controller;
private final ScheduledExecutorService detectorExecutor;
private volatile boolean running = false;
public HotParamDetector(HotParamFlowController controller) {
this.controller = controller;
this.detectorExecutor = Executors.newSingleThreadScheduledExecutor(
r -> new Thread(r, "hot-param-detector"));
}
public void start() {
if (running) {
return;
}
running = true;
// 每10秒检测一次热点
detectorExecutor.scheduleAtFixedRate(
this::detectHotParams, 10, 10, TimeUnit.SECONDS);
}
public void stop() {
running = false;
detectorExecutor.shutdown();
}
/**
* 检测热点参数
*/
private void detectHotParams() {
try {
// 获取热点参数
List<Map.Entry<Object, Double>> hotParams = controller.getHotParams(10);
if (!hotParams.isEmpty()) {
log.info("Detected hot params: {}", hotParams);
// 可以在这里触发告警或自动调整规则
triggerHotParamAlert(hotParams);
}
} catch (Exception e) {
log.error("Error detecting hot params", e);
}
}
/**
* 触发热点参数告警
*/
private void triggerHotParamAlert(List<Map.Entry<Object, Double>> hotParams) {
// 发送告警
AlertMessage alert = AlertMessage.builder()
.type("HOT_PARAM_ALERT")
.level("WARNING")
.title("热点参数检测告警")
.content(String.format("检测到%d个热点参数", hotParams.size()))
.details(hotParams)
.timestamp(System.currentTimeMillis())
.build();
// 发送到告警系统
alertService.sendAlert(alert);
// 可以自动调整限流规则
adjustRulesForHotParams(hotParams);
}
/**
* 为热点参数调整规则
*/
private void adjustRulesForHotParams(List<Map.Entry<Object, Double>> hotParams) {
for (Map.Entry<Object, Double> entry : hotParams) {
Object paramValue = entry.getKey();
double hotScore = entry.getValue();
// 根据热度调整限流阈值
double newThreshold = calculateNewThreshold(hotScore);
// 更新规则
updateRuleForParam(paramValue, newThreshold);
}
}
/**
* 计算新的限流阈值
*/
private double calculateNewThreshold(double hotScore) {
// 热度越高,阈值越低(限流更严格)
if (hotScore > 1000) {
return 10; // 极高热度,严格限流
} else if (hotScore > 500) {
return 50; // 高热度,较强限流
} else if (hotScore > 100) {
return 100; // 中等热度,适度限流
} else {
return 200; // 低热度,宽松限流
}
}
}
/**
* 滑动窗口计数器
*/
public class SlidingWindowCounter {
// 窗口长度(毫秒)
private final int windowLengthInMs;
// 窗口间隔(毫秒)
private final int intervalInMs;
// 窗口数量
private final int windowCount;
// 窗口数组
private final AtomicReferenceArray<Window> windows;
public SlidingWindowCounter(int windowLengthInMs, int intervalInMs) {
this.windowLengthInMs = windowLengthInMs;
this.intervalInMs = intervalInMs;
this.windowCount = windowLengthInMs / intervalInMs;
this.windows = new AtomicReferenceArray<>(windowCount);
}
/**
* 增加通过计数
*/
public void addPass(int count) {
Window window = getCurrentWindow();
window.addPass(count);
}
/**
* 增加阻塞计数
*/
public void addBlock(int count) {
Window window = getCurrentWindow();
window.addBlock(count);
}
/**
* 获取当前窗口内的通过QPS
*/
public long passQps() {
long currentTime = System.currentTimeMillis();
long windowStart = currentTime - windowLengthInMs;
long passCount = 0;
for (int i = 0; i < windowCount; i++) {
Window window = windows.get(i);
if (window != null && window.getStartTime() > windowStart) {
passCount += window.getPassCount();
}
}
return passCount * 1000 / windowLengthInMs;
}
/**
* 获取总请求数
*/
public long totalRequests() {
long total = 0;
for (int i = 0; i < windowCount; i++) {
Window window = windows.get(i);
if (window != null) {
total += window.getTotalCount();
}
}
return total;
}
/**
* 获取当前时间窗口
*/
private Window getCurrentWindow() {
long currentTime = System.currentTimeMillis();
int idx = calculateWindowIdx(currentTime);
long windowStart = calculateWindowStart(currentTime);
while (true) {
Window oldWindow = windows.get(idx);
if (oldWindow == null) {
// 创建新窗口
Window newWindow = new Window(windowStart);
if (windows.compareAndSet(idx, null, newWindow)) {
return newWindow;
} else {
// 并发创建,重试
Thread.yield();
}
} else if (oldWindow.getStartTime() == windowStart) {
// 当前窗口
return oldWindow;
} else if (oldWindow.getStartTime() < windowStart) {
// 旧窗口,需要重置
Window newWindow = new Window(windowStart);
if (windows.compareAndSet(idx, oldWindow, newWindow)) {
return newWindow;
} else {
// 并发更新,重试
Thread.yield();
}
} else {
// 时间回退,理论上不会发生
return new Window(windowStart);
}
}
}
private int calculateWindowIdx(long timeMillis) {
long timeId = timeMillis / intervalInMs;
return (int)(timeId % windowCount);
}
private long calculateWindowStart(long timeMillis) {
return timeMillis - timeMillis % intervalInMs;
}
/**
* 时间窗口
*/
private static class Window {
private final long startTime;
private final AtomicLong passCount = new AtomicLong(0);
private final AtomicLong blockCount = new AtomicLong(0);
public Window(long startTime) {
this.startTime = startTime;
}
public void addPass(int count) {
passCount.addAndGet(count);
}
public void addBlock(int count) {
blockCount.addAndGet(count);
}
public long getPassCount() {
return passCount.get();
}
public long getBlockCount() {
return blockCount.get();
}
public long getTotalCount() {
return passCount.get() + blockCount.get();
}
public long getStartTime() {
return startTime;
}
}
}
/**
* 热点参数限流使用示例
*/
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private HotParamFlowController hotParamController;
/**
* 热点商品查询接口
* 对商品ID进行热点参数限流
*/
@GetMapping("/{productId}")
@SentinelResource(
value = "getProductDetail",
blockHandler = "handleBlock",
fallback = "handleFallback"
)
public ProductDetail getProductDetail(@PathVariable Long productId,
@RequestParam(required = false) String userId) {
// 1. 检查热点参数限流
HotParamRule rule = new HotParamRule();
rule.setResource("getProductDetail");
rule.setParamIdx(0); // 第一个参数:productId
rule.setCount(100); // 默认QPS限制
rule.setDurationInSec(1);
// 2. 为特定热点商品设置更严格的限制
if (isHotProduct(productId)) {
rule.addSpecificRule(productId, 10.0); // 热点商品限制10QPS
}
// 3. 检查是否通过
Object[] args = {productId, userId};
if (!hotParamController.canPass("getProductDetail", args, rule)) {
throw new RateLimitException("商品访问频率过高,请稍后重试");
}
// 4. 执行业务逻辑
return productService.getProductDetail(productId);
}
/**
* 订单创建接口
* 对用户ID进行热点参数限流
*/
@PostMapping("/orders")
public Order createOrder(@RequestBody CreateOrderRequest request) {
// 1. 热点参数:用户ID
HotParamRule userRule = new HotParamRule();
userRule.setResource("createOrder");
userRule.setParamIdx(0); // 用户ID
userRule.setCount(50); // 默认限制
// 2. 热点参数:商品ID
HotParamRule productRule = new HotParamRule();
productRule.setResource("createOrder");
productRule.setParamIdx(1); // 商品ID
productRule.setCount(100); // 默认限制
// 3. 双重检查
Object[] args = {request.getUserId(), request.getProductId()};
if (!hotParamController.canPass("createOrder", args, userRule) ||
!hotParamController.canPass("createOrder", args, productRule)) {
throw new RateLimitException("操作过于频繁,请稍后重试");
}
// 4. 执行业务逻辑
return orderService.createOrder(request);
}
/**
* 热点商品检测
*/
private boolean isHotProduct(Long productId) {
// 这里可以实现热点商品检测逻辑
// 例如:从缓存中获取商品热度
String cacheKey = "hot:product:" + productId;
Integer hotScore = redisTemplate.opsForValue().get(cacheKey);
return hotScore != null && hotScore > 100;
}
/**
* 阻塞处理器
*/
public ProductDetail handleBlock(Long productId, String userId, BlockException ex) {
log.warn("商品详情接口被限流,productId: {}, userId: {}", productId, userId);
// 返回兜底数据
ProductDetail fallback = new ProductDetail();
fallback.setProductId(productId);
fallback.setProductName("商品详情暂时不可用");
fallback.setAvailable(false);
fallback.setFallback(true);
return fallback;
}
/**
* 降级处理器
*/
public ProductDetail handleFallback(Long productId, String userId, Throwable t) {
log.error("商品详情接口降级,productId: {}, userId: {}", productId, userId, t);
// 返回降级数据
ProductDetail fallback = new ProductDetail();
fallback.setProductId(productId);
fallback.setProductName("服务暂时不可用,请稍后重试");
fallback.setAvailable(false);
fallback.setFallback(true);
return fallback;
}
}
}
思考 :Sentinel的流量控制哲学已经从传统的"被动防御"演进为"主动治理"。记住三个核心认知:1) SlotChain不是简单的过滤器链,而是可插拔的治理框架 ,每个Slot都是一个治理维度;2) QPS和并发数限流解决的是不同维度的问题 ,QPS控制"速率",并发数控制"资源";3) 热点参数限流的本质是精准的资源分配 ,让有限的系统资源服务更多的用户。真正的流量治理不是简单地拒绝请求,而是智能地分配请求,优雅地降级服务,动态地调整策略。
如果觉得本文对你有帮助,请点击 👍 点赞 + ⭐ 收藏 + 💬 留言支持!
讨论话题:
- 你在项目中是如何使用Sentinel进行流量治理的?
- 如何选择合适的限流算法?
- 热点参数限流有哪些最佳实践?
相关资源推荐:
- 📚 https://sentinelguard.io/zh-cn/ - 最权威的Sentinel学习资料
- 📚 https://microservices.io/ - 微服务架构权威指南
- 💻 https://github.com/alibaba/Sentinel - 源代码学习
- 🎬 https://time.geekbang.org/course/intro/100064101 - 极客时间Sentinel课程