九、sentinel处理链核心源码分析(二)

本文深入解析Sentinel流量控制组件的工作原理,包括直接拒绝、排队等待和预热模式。详细介绍了流控检查流程,如检查资源对应的限流规则,使用令牌桶算法进行流控判断。同时,文章还涵盖了Sentinel的熔断机制,通过检查断路器状态,决定是否允许请求通过。Sentinel在保障系统稳定性方面展现出强大的能力。
摘要由CSDN通过智能技术生成

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时,使用了滑动窗口,漏斗算法,令牌桶算法等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值