sentinel学习(3)——源码阅读----SentinelWebInterceptor(完)

1:看下这个类实现了哪些接口跟类。

如图所示
xxxx
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讲解。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值