一,规则的创建
FlowRuleManager.loadRules(rules) 流控
DegradeRuleManager.loadRules(rules);降级
AuthorityRuleManager.loadRules(rules);校验
重点:
static {
currentProperty.addListener(LISTENER);//注册规则监听器
startMetricTimerListener();//开启定时任务
}
private static void startMetricTimerListener() {
//获取任务的时间间隔
long flushInterval = SentinelConfig.metricLogFlushIntervalSec();
if (flushInterval <= 0) {
RecordLog.info("[FlowRuleManager] The MetricTimerListener isn't started. If you want to start it, "
+ "please change the value(current: {}) of config({}) more than 0 to start it.", flushInterval,
SentinelConfig.METRIC_FLUSH_INTERVAL);
return;
}
SCHEDULER.scheduleAtFixedRate(new MetricTimerListener(), 0, flushInterval, TimeUnit.SECONDS);
}
MetricTimerListener :
public class MetricTimerListener implements Runnable {
private static final MetricWriter metricWriter = new MetricWriter(SentinelConfig.singleMetricFileSize(),
SentinelConfig.totalMetricFileCount());
@Override
public void run() {
Map<Long, List<MetricNode>> maps = new TreeMap<>();
for (Entry<ResourceWrapper, ClusterNode> e : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
ClusterNode node = e.getValue();
Map<Long, MetricNode> metrics = node.metrics();
aggregate(maps, metrics, node);
}
aggregate(maps, Constants.ENTRY_NODE.metrics(), Constants.ENTRY_NODE);
if (!maps.isEmpty()) {
for (Entry<Long, List<MetricNode>> entry : maps.entrySet()) {
try {
metricWriter.write(entry.getKey(), entry.getValue());
} catch (Exception e) {
RecordLog.warn("[MetricTimerListener] Write metric error", e);
}
}
}
}
private void aggregate(Map<Long, List<MetricNode>> maps, Map<Long, MetricNode> metrics, ClusterNode node) {
for (Entry<Long, MetricNode> entry : metrics.entrySet()) {
long time = entry.getKey();
MetricNode metricNode = entry.getValue();
metricNode.setResource(node.getName());
metricNode.setClassification(node.getResourceType());
maps.computeIfAbsent(time, k -> new ArrayList<MetricNode>());
List<MetricNode> nodes = maps.get(time);
nodes.add(entry.getValue());
}
}
}
二,核心业务的入口
entry = SphU.entry(KEY);key 为资源名称
三,加载spi扩展接口,进行环境初始化逻辑
List<InitFunc> initFuncs = SpiLoader.of(InitFunc.class).//对InitFunc接口的封装以及spiloader的缓存
loadInstanceListSorted()// 1, 使用类加载器(可能是当前线程的上下文加载器,也可能是该接口的加载器 根据SentinelConfig中的属性配置决定)加载META-INF/services/+接口的全名 文件获取该文件的url 获取其中配置的所有类,
2,而后根据反射机制进行类的加载,将获取到的class进行缓存
3,根据是否是单例模式,使用反射技术进行对象的创建并返回
不同点:在1.7版本中还会加载诸如发送心跳包之类的组件,在1.8中已经移除
for(InitFunc initFunc : initFuncs) insertSorted(initList, initFunc)//根据注解中配置的顺序,对spi中的接口实现进行排序
for (OrderWrapper w : initList) w.func.init()//对spi扩展的实现一次调用其方法
1,拓展点com.alibaba.csp.sentinel.init.InitFunc的实现类 ##源码注释
com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit ##Register callbacks for metric extension.
2,com.alibaba.csp.sentinel.slotchain.ProcessorSlot的实现类 ##A container of some process and ways of notification when the process is finished.(责任链模式)
com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot
com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot
com.alibaba.csp.sentinel.slots.logger.LogSlot
com.alibaba.csp.sentinel.slots.statistic.StatisticSlot
com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot
com.alibaba.csp.sentinel.slots.system.SystemSlot
com.alibaba.csp.sentinel.slots.block.flow.FlowSlot
com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot
3,com.alibaba.csp.sentinel.slotchain.SlotChainBuilder的实现类 ##The builder for processor slot chain.(简化版的构建者模式)
com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder
Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0)
四,开始业务逻辑所需的上下文准备
Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0)
经过对资源的封装之后会进入到com.alibaba.csp.sentinel.CtSph
entryWithPriority(com.alibaba.csp.sentinel.slotchain.ResourceWrapper, int, boolean, java.lang.Object...) 方法 该部分源码如下
Context context = ContextUtil.getContext();//在当前线程中获取上下文容器(ThreadLocal)
if (context instanceof NullContext) {
// The {@link NullContext} indicates that the amount of context has exceeded the threshold,
// so here init the entry only. No rule checking will be done.
return new CtEntry(resourceWrapper, null, context);
}
if (context == null) {
// Using default context.
//使用默认的名字创建一个上下文容器 sentinel_default_context
//最终会调用到com.alibaba.csp.sentinel.context.ContextUtil#trueEnter(String name, String origin)在这里面做了如下几件事
1,DefaultNode node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);//见附件
2,Constants.ROOT.##new EntranceNode(new StringResourceWrapper(ROOT_ID, EntryType.IN),new ClusterNode(ROOT_ID, ResourceTypeConstants.COMMON));
addChild(node)
3,Map<String, DefaultNode> newMap = new HashMap<>(contextNameNodeMap.size()+1);
newMap.putAll(contextNameNodeMap);
newMap.put(name, node);//缓存 容器名与节点
contextNameNodeMap = newMap;
4,context = new Context(node, name)//对节点与名称的一个封装
5,contextHolder.set(context)//保存在当前线程的上下文环境
context=InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
}
// Global switch is close, no rule checking will do.
if (!Constants.ON) {
return new CtEntry(resourceWrapper, null, context);
}
//形成处理器链,最新版本使用spi机制进行创建
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
/*
* Means amount of resources (slot chain) exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE},
* so no rule checking will be done.
*/
if (chain == null) {
return new CtEntry(resourceWrapper, null, context);
}
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
//进入责任链模式,每个节点处理自己的业务逻辑
1,NodeSelectorSlot :DefaultNode node = new DefaultNode(resourceWrapper, null)
2,clusterNode = new ClusterNode(resourceWrapper.getName(),resourceWrapper.getResourceType());
node.setClusterNode(clusterNode)
if (!"".equals(context.getOrigin())) {
// statisticNode = new StatisticNode();
Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
context.getCurEntry().setOriginNode(originNode);
}
3,StatisticSlot :entry() 核心点
{
try {
// Do some checking. 继续调用后面的环节,检查是否超过规则的限制,如果没有超过,则进行数据统计,否则进入异常的处理环节
fireEntry(context, resourceWrapper, node, count, prioritized, args);
// Request passed, add thread count and pass count.时间窗口算法
node.increaseThreadNum();
node.addPassRequest(count);
if (context.getCurEntry().getOriginNode() != null) {
// Add count for origin 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.
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.
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;
}
}
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
e.exit(count, args);
throw e1;
} catch (Throwable e1) {
// This should not happen, unless there are errors existing in Sentinel internal.
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
}
五:后续的处理环节
fireEntry(context, resourceWrapper, node, count, prioritized, args);
以流量控制为例:
checkFlow(resourceWrapper, context, node, count, prioritized)
{
checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);
}
ruleProvider: private final Function<String, Collection<FlowRule>> ruleProvider = new Function<String, Collection<FlowRule>>() {
@Override
public Collection<FlowRule> apply(String resource) {
// Flow rule map should not be null.
Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();
return flowRules.get(resource);
}
};
public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,
Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {
if (ruleProvider == null || resource == null) {
return;
}
Collection<FlowRule> rules = ruleProvider.apply(resource.getName());
if (rules != null) {
for (FlowRule rule : rules) {
if (!canPassCheck(rule, context, node, count, prioritized)) {
throw new FlowException(rule.getLimitApp(), rule);
}
}
}
}
public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount,
boolean prioritized) {
String limitApp = rule.getLimitApp();
if (limitApp == null) {
return true;
}
//集群模式
if (rule.isClusterMode()) {
return passClusterCheck(rule, context, node, acquireCount, prioritized);
}
//单机模式
return passLocalCheck(rule, context, node, acquireCount, prioritized);
}
private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount,
boolean prioritized) {
Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node);
if (selectedNode == null) {
return true;
}
return rule.getRater().canPass(selectedNode, acquireCount, prioritized);
}
六,异常的处理环节
七,时间滑动门窗口
Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,//2
IntervalProperty.INTERVAL);//1000
public class ArrayMetric implements Metric{
private final LeapArray<MetricBucket> data;
public ArrayMetric(int sampleCount, int intervalInMs) {
this.data = new OccupiableBucketLeapArray(sampleCount, intervalInMs);
}
....省略其他部分
}
//通过的请求数目加一
rollingCounterInSecond.pass();
源码注释:Get total pass count. not include {@link #occupiedPass()}
public long pass() {
## Get the bucket at current timestamp.
public WindowWrap<T> currentWindow() {
return currentWindow(TimeUtil.currentTimeMillis());
}
public WindowWrap<T> currentWindow(long timeMillis) {
//当前窗口所在桶索引[0,1)
int idx = calculateTimeIdx(timeMillis);
// Calculate current bucket start time.
long windowStart = calculateWindowStart(timeMillis);
while (true) {
WindowWrap<T> old = array.get(idx);
if (old == null) {
//新建一个桶,并原子性的更新
WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));
if (array.compareAndSet(idx, null, window)) {
// Successfully updated, return the created bucket.
return window;
} else {
// Contention failed, the thread will yield its time slice to wait for bucket available.
Thread.yield();
}
}
} else if (windowStart == old.windowStart()) {
return old;
} else if (windowStart > old.windowStart()) {
if (updateLock.tryLock()) {
try {
// Successfully get the update lock, now we reset the bucket.
//当前时间戳在当前窗口期,更新窗口信息
return resetWindowTo(old, windowStart);
//
protected WindowWrap<MetricBucket> resetWindowTo(WindowWrap<MetricBucket> w, long time) {
// Update the start time and reset value.
w.resetTo(time);
MetricBucket borrowBucket = borrowArray.getWindowValue(time);
if (borrowBucket != null) {
w.value().reset();
//通过的请求统计
w.value().addPass((int)borrowBucket.pass());
} else {
w.value().reset();
}
return w;
}
} finally {
updateLock.unlock();
}
} else {
// Contention failed, the thread will yield its time slice to wait for bucket available.
Thread.yield();
}
}
else if (windowStart < old.windowStart()) {
// Should not go through here, as the provided time is already behind.
return new WindowWrap<T>(windowLengthInMs, windowStart,newEmptyBucket(timeMillis));
}
}
}
data.currentWindow();
long pass = 0;
List<MetricBucket> list = data.values();
for (MetricBucket window : list) {
pass += window.pass();
}
return pass;
}
附:EntranceNode
Context:
1.7中使用spi机制加载的InitFunc组件