chain.entry方法去依次执行chain上的slot
public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)
throws Throwable {
//调用first的transformEntry
first.transformEntry(context, resourceWrapper, t, count, prioritized, args);
}
transformEntry方法是父类AbstractLinkedProcessorSlot的方法.
void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)
throws Throwable {
T t = (T)o;
//调用first的entry
entry(context, resourceWrapper, t, count, prioritized, args);
}
entry方法
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)
throws Throwable {
//调用父类的fireEntry
super.fireEntry(context, resourceWrapper, t, count, prioritized, args);
}
AbstractLinkedProcessorSlot的fireEntry方法:
@Override
public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
throws Throwable {
//next的是NodeSelectorSlot
if (next != null) {
//又调用AbstractLinkedProcessorSlot的transformEntry方法
next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);
}
}
此时next的是NodeSelectorSlot,则会调用父类的transformEntry,里面再调用本身entry,
本身entry方法结束,就调用父类fireEntry。依次按刚才的执行规律,执行链路上的slot的entry。
我们只要看我们需要看的Slot的entry方法即可。
1、NodeSelectorSlot
这个主要是构建一个调用链路。
@Override
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) {
//Context 持有元数据的上下文,
//所谓的元数据主要是Entry,而Entry是一个双向链表,构成了一个调用链。
node = map.get(context.getName());
if (node == null) {
//如果当前上下文中没有该节点,则创建一个DefaultNode节点
node = new DefaultNode(resourceWrapper, null);
//这里使用copyAndWrite的方式
HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
cacheMap.putAll(map);
cacheMap.put(context.getName(), node);
map = cacheMap;
// 如果第一次调用entry,此时curEntry的parent是null,那么addChild就是追加到entranceNode入口node中
//后面再次调用时,addChild都是追加到parent的node中
((DefaultNode) context.getLastNode()).addChild(node);
}
}
}
//设置上下文的当前node
context.setCurNode(node);
//调用下一个Slot
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
NodeSelectorSlot的exit方法什么都没做。
2、ClusterBuilderSlot
@Override
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) {
// 创建集群节点.
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.setClusterNode(clusterNode);
if (!"".equals(context.getOrigin())) {
Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
context.getCurEntry().setOriginNode(originNode);
}
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
ClusterBuilderSlot的exit方法什么都没做。
3、LogSlot
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode obj, int count, boolean prioritized, Object... args)
throws Throwable {
try {
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
统计信息
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
try {
// 调用下一个Slot
fireEntry(context, resourceWrapper, node, count, prioritized, args);
//请求通过增加线程数量.
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;
}
}
5、AuthoritySlot
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
throws Throwable {
//匹配黑白名单
checkBlackWhiteAuthority(resourceWrapper, context);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
AuthoritySlot的exit方法什么都没做。
6、SystemSlot
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
//检查系统规则 --校验qps,线程数,cpu使用情况等
SystemRuleManager.checkSystem(resourceWrapper);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
SystemSlot的exit方法什么都没做。
7、FlowSlot 重点
@Override
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);
}
checkFlow方法:
void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)
throws BlockException {
checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);
}
FlowRuleChecker的checkFlow方法:
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;
}
//获取调用的resource对应的所有限流规则
Collection<FlowRule> rules = ruleProvider.apply(resource.getName());
if (rules != null) {
for (FlowRule rule : rules) {
//逐个规则判断是否触发限流操作 触发限流,直接抛出FlowException
if (!canPassCheck(rule, context, node, count, prioritized)) {
throw new FlowException(rule.getLimitApp(), rule);
}
}
}
}
canPassCheck方法
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);
}
passLocalCheck方法:
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;
}
根据配置FlowRule配置的controlBehavior(流控效果:直接拒绝、排队等待、 Warm Up)选择不同的Controller,判断是否通过
return rule.getRater().canPass(selectedNode, acquireCount, prioritized);
}
分三种情况:
(1)、直接拒绝
DefaultController的canPass方法
@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
//获取当前node节点的线程数或者请求的qps总数
int curCount = avgUsedTokens(node);
//当前已经请求数+申请总数是否大于该资源配置的阈值
if (curCount + acquireCount > count) {
if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) {
long currentTime;
long waitInMs;
currentTime = TimeUtil.currentTimeMillis();
waitInMs = node.tryOccupyNext(currentTime, acquireCount, count);
if (waitInMs < OccupyTimeoutProperty.getOccupyTimeout()) {
node.addWaitingRequest(currentTime + waitInMs, acquireCount);
node.addOccupiedPass(acquireCount);
sleep(waitInMs);
// PriorityWaitException indicates that the request will pass after waiting for {@link @waitInMs}.
throw new PriorityWaitException(waitInMs);
}
}
//大于就返回false
return false;
}
//小于阈值,通过
return true;
}
(2)、排队等待
RateLimiterController的canPass方法
@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
// 请求数小于等于0
if (acquireCount <= 0) {
return true;
}
//阈值小于0
if (count <= 0) {
return false;
}
long currentTime = TimeUtil.currentTimeMillis();
//计算每2个请求之间的间隔毫秒,比如QPS限制为10,那么间隔就是100ms
long costTime = Math.round(1.0 * (acquireCount) / count * 1000);
//期望下次的执行时间
long expectedTime = costTime + latestPassedTime.get();
if (expectedTime <= currentTime) {
//可以通过,设置latestPassedTime然后就返回true了
latestPassedTime.set(currentTime);
return true;
} else {
//不可以通过,需要等待
// 重新计算等待时间
long waitTime = costTime + latestPassedTime.get() - TimeUtil.currentTimeMillis();
//等待时长大于设置的最大超时时间,返回false
if (waitTime > maxQueueingTimeMs) {
return false;
} else {
//再次重新计算下需要执行的时间
long oldTime = latestPassedTime.addAndGet(costTime);
try {
//获取等待时间
waitTime = oldTime - TimeUtil.currentTimeMillis();
if (waitTime > maxQueueingTimeMs) {
latestPassedTime.addAndGet(-costTime);
return false;
}
// in race condition waitTime may <= 0
if (waitTime > 0) {
//睡眠等待
Thread.sleep(waitTime);
}
return true;
} catch (InterruptedException e) {
}
}
}
return false;
}
(3)、Warm Up
WarmUpController的canPass方法,令牌桶算法。
//构造
public WarmUpController(double count, int warmUpPeriodInSec, int coldFactor) {
construct(count, warmUpPeriodInSec, coldFactor);
}
public WarmUpController(double count, int warmUpPeriodInSec) {
construct(count, warmUpPeriodInSec, 3);
}
private void construct(double count, int warmUpPeriodInSec, int coldFactor) {
if (coldFactor <= 1) {
throw new IllegalArgumentException("Cold factor should be larger than 1");
}
this.count = count;
this.coldFactor = coldFactor;
// thresholdPermits = 0.5 * warmupPeriod / stableInterval.
// warningToken = 100;
warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1);
// / maxPermits = thresholdPermits + 2 * warmupPeriod /
// (stableInterval + coldInterval)
// maxToken = 200
maxToken = warningToken + (int)(2 * warmUpPeriodInSec * count / (1.0 + coldFactor));
// slope
// slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits
// - thresholdPermits);
slope = (coldFactor - 1.0) / count / (maxToken - warningToken);
}
@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
long passQps = (long) node.passQps();
long previousQps = (long) node.previousPassQps();
syncToken(previousQps);
// 开始计算它的斜率
// 如果进入了警戒线,开始调整他的qps
long restToken = storedTokens.get();
if (restToken >= warningToken) {
long aboveToken = restToken - warningToken;
// 消耗的速度要比warning快,但是要比慢
// current interval = restToken*slope+1/count
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;
}
FlowSlot的exit方法什么都没做。
8、DegradeSlot
@Override
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);
}
performChecking方法
void performChecking(Context context, ResourceWrapper r) throws BlockException {
//熔断器
List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
if (circuitBreakers == null || circuitBreakers.isEmpty()) {
return;
}
for (CircuitBreaker cb : circuitBreakers) {
//是否通过
if (!cb.tryPass(context)) {
throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());
}
}
}
AbstractCircuitBreaker的tryPass方法:
@Override
public boolean tryPass(Context context) {
// 开关是关闭的,返回true
if (currentState.get() == State.CLOSED) {
return true;
}
//是打开的
if (currentState.get() == State.OPEN) {
//没超时就返回false,超时再将开关从开状态转换成半开状态
return retryTimeoutArrived() && fromOpenToHalfOpen(context);
}
return false;
}
DegradeSlot的exit方法,就不看了,简单总结下会调用断路器的onRequestComplete方法,在半开状态,根据时间或异常信息判断,重置断路器状态。
exit方法就先不看了。总之sentinel的chain在执行slot时,使用了滑动窗口,漏斗算法,令牌桶算法等。