1:看下这个类实现了哪些接口跟类。
如图所示
HandlerInterceptor 这个接口大家应该都知道了,我们直接看三个方法:preHandle,afterCompletion,postHandle(这个方法没有做操作。)
2:preHandle()
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
//这里是获取请求路径。 这里会经过 UrlCleaner接口的处理,感觉这里可以自由配置处理,例如设置黑白名单这种,
String resourceName = getResourceName(request);
if (StringUtil.isNotEmpty(resourceName)) {
// Parse the request origin using registered origin parser.
//这里也可以自由实现 RequestOriginParser 接口,实现自己自定义的解析 origin 信息
String origin = parseOrigin(request);
// 这里面大概就是在ThreadLocal中set一个Context上下文的对象。
//补充 这里面漏掉了 一些重要的逻辑。
ContextUtil.enter(SENTINEL_SPRING_WEB_CONTEXT_NAME, origin);
//这里就是比较关键 获取令牌的逻辑了,
Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
//这里就是把entry对象set到request的属性中。
setEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName(), entry);
}
return true;
} catch (BlockException e) {
//上面的内容如果抛出异常,这里捕捉,然后使用默认或者 自定义的 实现去处理。这里不详细讲解了,一看就明白了。
handleBlockException(request, response, e);
return false;
}
}
2.1 ContextUtil.enter(SENTINEL_SPRING_WEB_CONTEXT_NAME, origin);
//直接进入主方法
protected static Context trueEnter(String name, String origin) {
Context context = contextHolder.get();
if (context == null) {
Map<String, DefaultNode> localCacheNameMap = contextNameNodeMap;
DefaultNode node = localCacheNameMap.get(name);
if (node == null) {
if (localCacheNameMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
setNullContext();
return NULL_CONTEXT;
} else {
try {
LOCK.lock();
node = contextNameNodeMap.get(name);
if (node == null) {
if (contextNameNodeMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
setNullContext();
return NULL_CONTEXT;
} else {
// 这里是会先生成一个 EntranceNode (入口节点)节点。作为根目录的 子节点。
node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);
// Add entrance node.
Constants.ROOT.addChild(node);
Map<String, DefaultNode> newMap = new HashMap<>(contextNameNodeMap.size() + 1);
newMap.putAll(contextNameNodeMap);
newMap.put(name, node);
contextNameNodeMap = newMap;
}
}
} finally {
LOCK.unlock();
}
}
}
//把 生成的节点 保存到 上下文 context中。
context = new Context(node, name);
context.setOrigin(origin);
contextHolder.set(context);
}
return context;
}
3: 重点讲解一下:Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
这个方法进去以后 这里直接来到 下面的代码:各种跳转的不细说。
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
//这个拿到上面创建的 Context上下文。
Context context = ContextUtil.getContext();
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);
}
//这里是创建 默认的内置的 sentinel_default_context 的Context。到这里是会有值的。
if (context == null) {
// Using default 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);
}
//查看处理链条。这里很关键。下面细讲。
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 {
//这里就是调用 上面构造出来调用链中每个Slot。下一章节将按照每一个Slot讲解。
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;
}
4:lookProcessChain(resourceWrapper);
ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
// 这个chainMap是 volatile Map<ResourceWrapper, ProcessorSlotChain>
ProcessorSlotChain chain = chainMap.get(resourceWrapper);
if (chain == null) {
synchronized (LOCK) {
chain = chainMap.get(resourceWrapper);
if (chain == null) {
// Entry size limit.
if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
return null;
}
//这里是创建槽位处理链条。具体逻辑看下面的逻辑。
chain = SlotChainProvider.newSlotChain();
//下面是把这个调用链缓存起来。
Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
chainMap.size() + 1);
newMap.putAll(chainMap);
newMap.put(resourceWrapper, chain);
chainMap = newMap;
}
}
}
return chain;
}
SlotChainProvider.newSlotChain();------------->讲解:
public static ProcessorSlotChain newSlotChain() {
if (slotChainBuilder != null) {
return slotChainBuilder.build();
}
// Resolve the slot chain builder SPI.
//看方法名称:这里应该会 获取到 HotParamSlotChainBuilder的类,取的是第一个实例。没有debug去看了
slotChainBuilder = SpiLoader.loadFirstInstanceOrDefault(SlotChainBuilder.class, DefaultSlotChainBuilder.class);
if (slotChainBuilder == null) {
// Should not go through here.
RecordLog.warn("[SlotChainProvider] Wrong state when resolving slot chain builder, using default");
slotChainBuilder = new DefaultSlotChainBuilder();
} else {
RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: "
+ slotChainBuilder.getClass().getCanonicalName());
}
//看看 HotParamSlotChainBuilder 构建的调用链:
//ProcessorSlotChain chain = new DefaultProcessorSlotChain();
//chain.addLast(new NodeSelectorSlot());
//chain.addLast(new ClusterBuilderSlot());
//chain.addLast(new LogSlot());
//chain.addLast(new StatisticSlot());
//chain.addLast(new ParamFlowSlot());
//chain.addLast(new SystemSlot());
//chain.addLast(new AuthoritySlot());
//chain.addLast(new FlowSlot());
//chain.addLast(new DegradeSlot());
return slotChainBuilder.build();
}
//至此 拦截链路生成了。下一章节将按照每一个Slot讲解。