构建slot链处理请求
//com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain#entry
public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)
throws Throwable {
first.transformEntry(context, resourceWrapper, t, count, prioritized, args);
}
//com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot#transformEntry
//频繁调用
void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)
throws Throwable {
T t = (T)o;
//调用上面first头节点的entry,因为匿名内部类里面重写了entry方法。
entry(context, resourceWrapper, t, count, prioritized, args);
}
//com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot#fireEntry
//频繁调用
public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
throws Throwable {
//NodeSelectorSlot
if (next != null) {
//调的上面的transformEntry
next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);
}
}
1、NodeSelectorSlot
//com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot#entry
//NodeSelectorSlot order=-10000
private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);
//进入entry方法,同一个资源名称会有不同的context,context的名称作为key,每个context有一个DefaultNode,不同context的DefaultNode共享一个ClusterNode
public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
throws Throwable {
DefaultNode node = map.get(context.getName());
if (node == null) {
synchronized (this) {
node = map.get(context.getName());
if (node == null) {
//当前context没有DefaultNode,创建,然后加入缓存
node = new DefaultNode(resourceWrapper, null);
HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
cacheMap.putAll(map);
cacheMap.put(context.getName(), node);
map = cacheMap;
// Build invocation tree
//创建Node树,获取
((DefaultNode) context.getLastNode()).addChild(node);
}
}
}
//设置当前处理的Node
context.setCurNode(node);
//调用上面的fireEntry
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
//com.alibaba.csp.sentinel.context.Context#getLastNode
//context的根Node
private DefaultNode entranceNode;
//context当前处理的Node
private Entry curEntry;
public Node getLastNode() {
if (curEntry != null && curEntry.getLastNode() != null) {
return curEntry.getLastNode();
} else {
return entranceNode;
}
}
//com.alibaba.csp.sentinel.CtEntry#getLastNode
public Node getLastNode() {
return parent == null ? null : parent.getCurNode();
}
2、ClusterBuilderSlot
//com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot#entry
//缓存
private static volatile Map<ResourceWrapper, ClusterNode> clusterNodeMap = new HashMap<>();
private static final Object lock = new Object();
private volatile ClusterNode clusterNode = null;
//如果clusterNode为空,则创建
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args)
throws Throwable {
if (clusterNode == null) {
synchronized (lock) {
if (clusterNode == null) {
// Create the cluster node.
//创建clusterNode
clusterNode = new ClusterNode(resourceWrapper.getName(), resourceWrapper.getResourceType());
HashMap<ResourceWrapper, ClusterNode> newMap = new HashMap<>(Math.max(clusterNodeMap.size(), 16));
newMap.putAll(clusterNodeMap);
newMap.put(node.getId(), clusterNode);
clusterNodeMap = newMap;
}
}
}
//给当前Node设置clusterNode
node.setClusterNode(clusterNode);
/*
* if context origin is set, we should get or create a new {@link Node} of
* the specific origin.
*/
//如果源为空,则创建
if (!"".equals(context.getOrigin())) {
Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
context.getCurEntry().setOriginNode(originNode);
}
//调用上面的方法,执行下个slot
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
获取或者创建源Node
//com.alibaba.csp.sentinel.node.ClusterNode#getOrCreateOriginNode
private Map<String, StatisticNode> originCountMap = new HashMap<>();
public Node getOrCreateOriginNode(String origin) {
//缓存中没有则创建
StatisticNode statisticNode = originCountMap.get(origin);
if (statisticNode == null) {
lock.lock();
try {
statisticNode = originCountMap.get(origin);
if (statisticNode == null) {
// The node is absent, create a new node for the origin.
statisticNode = new StatisticNode();
HashMap<String, StatisticNode> newMap = new HashMap<>(originCountMap.size() + 1);
newMap.putAll(originCountMap);
newMap.put(origin, statisticNode);
originCountMap = newMap;
}
} finally {
lock.unlock();
}
}
return statisticNode;
}
3、LogSlot
//com.alibaba.csp.sentinel.slots.logger.LogSlot#entry
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode obj, int count, boolean prioritized, Object... args)
throws Throwable {
try {
//调用上面的方法执行一个slot
fireEntry(context, resourceWrapper, obj, count, prioritized, args);
} catch (BlockException e) {
EagleEyeLogUtil.log(resourceWrapper.getName(), e.getClass().getSimpleName(), e.getRuleLimitApp(),
context.getOrigin(), count);
throw e;
} catch (Throwable e) {
RecordLog.warn("Unexpected entry exception", e);
}
}
4、StatisticSlot
//com.alibaba.csp.sentinel.slots.statistic.StatisticSlot#entry
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
try {
// Do some checking. 检查
fireEntry(context, resourceWrapper, node, count, prioritized, args);
// Request passed, add thread count and pass count.
//规则都校验完了,增加线程数,
node.increaseThreadNum();
node.addPassRequest(count);
//当前entry的源Node不为空
if (context.getCurEntry().getOriginNode() != null) {
// Add count for origin node.
//增加源Node的线程数量和通过的请求数
context.getCurEntry().getOriginNode().increaseThreadNum();
context.getCurEntry().getOriginNode().addPassRequest(count);
}
//输入类型 增加线程数和通过的请求数
if (resourceWrapper.getEntryType() == EntryType.IN) {
// Add count for global inbound entry node for global statistics.
Constants.ENTRY_NODE.increaseThreadNum();
Constants.ENTRY_NODE.addPassRequest(count);
}
// Handle pass event with registered entry callback handlers.
//请求通过,handler处理
for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onPass(context, resourceWrapper, node, count, args);
}
} catch (PriorityWaitException ex) {
node.increaseThreadNum();
if (context.getCurEntry().getOriginNode() != null) {
// Add count for origin node.
context.getCurEntry().getOriginNode().increaseThreadNum();
}
if (resourceWrapper.getEntryType() == EntryType.IN) {
// Add count for global inbound entry node for global statistics.
Constants.ENTRY_NODE.increaseThreadNum();
}
// Handle pass event with registered entry callback handlers.
for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onPass(context, resourceWrapper, node, count, args);
}
} catch (BlockException e) {
// Blocked, set block exception to current entry.
context.getCurEntry().setBlockError(e);
// Add block count.
node.increaseBlockQps(count);
if (context.getCurEntry().getOriginNode() != null) {
context.getCurEntry().getOriginNode().increaseBlockQps(count);
}
if (resourceWrapper.getEntryType() == EntryType.IN) {
// Add count for global inbound entry node for global statistics.
Constants.ENTRY_NODE.increaseBlockQps(count);
}
// Handle block event with registered entry callback handlers.
//StatisticSlotCallbackRegistry 添加 callback 在InitExecutor#doInit完成
for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onBlocked(e, context, resourceWrapper, node, count, args);
}
throw e;
} catch (Throwable e) {
// Unexpected internal error, set error to current entry.
context.getCurEntry().setError(e);
throw e;
}
}
4.1、执行slot链的剩下slot
4.1.1、AuthoritySlot
//com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot#entry
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
throws Throwable {
//检查黑白名单
checkBlackWhiteAuthority(resourceWrapper, context);
//执行下一个slot
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
4.1.2、SystemSlot
//com.alibaba.csp.sentinel.slots.system.SystemSlot#entry
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
SystemRuleManager.checkSystem(resourceWrapper);
//下一个节点
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
4.1.3、GatewayFlowSlot
和gateway集成后使用的限流slot
//com.alibaba.csp.sentinel.adapter.gateway.common.slot.GatewayFlowSlot#entry
public void entry(Context context, ResourceWrapper resource, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
//检查gatway的限流参数
checkGatewayParamFlow(resource, count, args);
//下一个节点
fireEntry(context, resource, node, count, prioritized, args);
}
4.1.4、ParamFlowSlot
//com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot#entry
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
//该资源没有参数限流规则,直接进入下个节点
if (!ParamFlowRuleManager.hasRules(resourceWrapper.getName())) {
//下个节点
fireEntry(context, resourceWrapper, node, count, prioritized, args);
return;
}
//校验
checkFlow(resourceWrapper, count, args);
//下个节点
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
4.1.5、FlowSlot
//com.alibaba.csp.sentinel.slots.block.flow.FlowSlot#entry
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
//校验
checkFlow(resourceWrapper, context, node, count, prioritized);
//下个节点
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
4.1.6、DegradeSlot 最后一个slot
//com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot#entry
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
//校验
performChecking(context, resourceWrapper);
//下个节点
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
这里通过了,又回到StatisticSlot#entry。
4.2、通过的线程数
//com.alibaba.csp.sentinel.node.DefaultNode#increaseThreadNum
public void increaseThreadNum() {
super.increaseThreadNum();
this.clusterNode.increaseThreadNum();
}
//com.alibaba.csp.sentinel.node.StatisticNode#increaseThreadNum
private LongAdder curThreadNum = new LongAdder();
public void increaseThreadNum() {
curThreadNum.increment();
}
4.3、增加通过的请求数
public void addPassRequest(int count) {
super.addPassRequest(count);
this.clusterNode.addPassRequest(count);
}
//com.alibaba.csp.sentinel.node.StatisticNode#addPassRequest
//两个监控指标,秒和分钟
private transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,IntervalProperty.INTERVAL);
private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false);
public void addPassRequest(int count) {
rollingCounterInSecond.addPass(count);
rollingCounterInMinute.addPass(count);
}
//com.alibaba.csp.sentinel.slots.statistic.metric.ArrayMetric#addPass
//滑动窗口
private final LeapArray<MetricBucket> data;
public void addPass(int count) {
//获取当前窗口
WindowWrap<MetricBucket> wrap = data.currentWindow();
wrap.value().addPass(count);
}
增加对应窗口的计数
//com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket#addPass
public void addPass(int n) {
add(MetricEvent.PASS, n);
}
每个滑动窗口中的计数是使用这样的数组做的,不同事件时不同的数组元素,可以增加并发读写
LongAdder是一个线程安全的long。
//com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket#add
private final LongAdder[] counters;
public MetricBucket add(MetricEvent event, long n) {
counters[event.ordinal()].add(n);
return this;
}
4.4、callback
MetricEntryCallback
请求通过 handler
//com.alibaba.csp.sentinel.metric.extension.callback.MetricEntryCallback#onPass
public void onPass(Context context, ResourceWrapper rw, DefaultNode param, int count, Object... args)
throws Exception {
//MetricExtensionProvider.getMetricExtensions() 没有东西
for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
if (m instanceof AdvancedMetricExtension) {
((AdvancedMetricExtension) m).onPass(rw, count, args);
} else {
m.increaseThreadNum(rw.getName(), args);
m.addPass(rw.getName(), count, args);
}
}
}
ParamFlowStatisticEntryCallback
//com.alibaba.csp.sentinel.slots.statistic.ParamFlowStatisticEntryCallback#onPass
public void onPass(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args) {
// The "hot spot" parameter metric is present only if parameter flow rules for the resource exist.
ParameterMetric parameterMetric = ParameterMetricStorage.getParamMetric(resourceWrapper);
//parameterMetric 是 null
if (parameterMetric != null) {
parameterMetric.addThreadCount(args);
}
}
5、处理链的顺序
sentinel processSlot链中slot的顺序:NodeSelectorSlot --> ClusterBuilderSlot --> LogSlot --> StatisticSlot --> AuthoritySlot --> SystemSlot --> GatewayFlowSlot(集成gatway) --> ParamFlowSlot --> FlowSlot --> DegradeSlot
从AuthoritySlot 开始,后面会根据规则校验当前请求。