sentinel工作流程2

各个ProcesserSlot分析

• NodeSelectorSlot:
选择一个统计Node节点放置当前的curEntry中,entry方法

        DefaultNode node = map.get(context.getName());
        if (node == null) {
            synchronized (this) {
                node = map.get(context.getName());
                if (node == null) {
                    node = new DefaultNode(resourceWrapper, null);
                    HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
                    cacheMap.putAll(map);
                    cacheMap.put(context.getName(), node);
                    map = cacheMap;
                }
                // Build invocation tree
                ((DefaultNode)context.getLastNode()).addChild(node);
            }
        }
        // context的curEntry的当前节点设置成该node
        context.setCurNode(node);
        fireEntry(context, resourceWrapper, node, count, prioritized, args);

DefaultNode是通过context中name得到,然后放入到map中。之前说过,一个ResourceWapper对应一个Chain对象,也说明了一个ResourceWapper对应一个NodeSelectorSlot对象,由于context创建可以设置不同的name,所以导致了一个ResourceWapper可能得到不同的DefaultNode对象,不同的context文本设置的curEntry的Node也是不一样的。在执行fireEntry方法时,将obj对象设置成了DefaultNode了。
• ClusterBuilderSlot:entry方法

        // 创建一个 集群节点
        if (clusterNode == null) {
            synchronized (lock) {
                if (clusterNode == null) {
                    // Create the cluster node.
                    clusterNode = new ClusterNode();
                    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())) {
            // origin有值,则需要对他创建 统计node,并且放入到clusterNode的originCountMap中
            Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
            // 将当前的entry设置origin节点
            context.getCurEntry().setOriginNode(originNode);
        }
        fireEntry(context, resourceWrapper, node, count, prioritized, args);

clusterNode是ClusterBuilderSlot的属性,所以一个ResourceWapper对应一个clusterNode对象,并且设置了DefaultNode对象中的clusterNode对象,如果存在origin来源标识,那么通过clusterNode去创建一个与origin关联的Node。从这里看出clusterNode作用,他是记录同一个resource的所有统计信息,与context没有关系。

• StatisticSlot:记录事件功能
它是各个事件统计的入口,例如通过各个processorSlot后,增加线程,增加qps,或者出现block异常时,增加blockQps。在退出方法中,增加相应耗时记录,减少线程等。部分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) {
                // 针对当前entry对象对应的origin节点进行增加
                // Add count for origin node.
                context.getCurEntry().getOriginNode().increaseThreadNum();
                context.getCurEntry().getOriginNode().addPassRequest(count);
            }

            if (resourceWrapper.getType() == EntryType.IN) {
                // 增对系统会输入状态的节点进行增加
                // Add count for global inbound entry node for global statistics.
                Constants.ENTRY_NODE.increaseThreadNum();
                Constants.ENTRY_NODE.addPassRequest(count);
            }
} catch(BlockException e) {
  // .....
}

首先StatisticSlot是先去调用其他ProcessorSlot结束后,才开始执行自身的逻辑。该统计,不仅仅只是使用当前node进行记录,有curEntry中对应的OriginNode也会进行记录,还有一个Constants.ENTRY_NODE 该节点是整个服务器的记录节点,记录整个服务器的请求信息,为什么需要他?可以认为系统也需要限流,保证系统的稳定。
在exit方法中

      if (context.getCurEntry().getError() == null) {
            // Calculate response time (max RT is TIME_DROP_VALVE).
            long rt = TimeUtil.currentTimeMillis() - context.getCurEntry().getCreateTime();
            if (rt > Constants.TIME_DROP_VALVE) {
                rt = Constants.TIME_DROP_VALVE;
            }
            // Record response time and success count.
            node.addRtAndSuccess(rt, count);
            if (context.getCurEntry().getOriginNode() != null) {
                context.getCurEntry().getOriginNode().addRtAndSuccess(rt, count);
            }
            node.decreaseThreadNum();
            if (context.getCurEntry().getOriginNode() != null) {
                context.getCurEntry().getOriginNode().decreaseThreadNum();
            }
            if (resourceWrapper.getType() == EntryType.IN) {
                Constants.ENTRY_NODE.addRtAndSuccess(rt, count);
                Constants.ENTRY_NODE.decreaseThreadNum();
            }
        } else {
            // Error may happen.
        }

在退出方法中,也是相关节点记录的也是需要记录,并且在没有异常的情况下,例如Block异常是不需要进行统计在里面的。这里可能会产生误区,该error异常,是通过processorSlot执行过程中产生的,所以对于业务逻辑出现异常,他是不关心的。该记录还是需要记录。因为业务逻辑产生的异常,无法控制,不过可以通过抓取异常,进行降级处理,也是不错的方案。

• SystemSlot:系统处理槽
监控系统情况,主要通过SystemRuleManager的checkSystem方法,通过监控系统的负载,cpu使用情况等,进行check系统本身,如果不通过就抛出SystemBlockException异常。当然,sentinel设计该检测服务器状态,不仅仅应该操作系统的本身负载,cpu等信息。还会通过Constants.ENTRY_NODE 该节点的统计信息,也作为参考指标。该设计原因,github,作者也有阐述:
https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81

• AuthoritySlot:黑白名单限制
主要通过AuthorityRule规则,设置白名单或者黑名单状态,并且通过context得到origin请求源,进行限定。主要是在消费者,生产者中关系最为常见。

• DegradeSlot:降级处理槽
主要通过DegradeRule规则进行限定,有响应耗时,异常比例,异常数量等等模式去控制。并且通过当前resource获取ClusterNode节点统计信息,作为参考数值。当被限制时,会将cut状态设置为true,将后面的调用都拒绝。但是cut的状态会通过重置线程,一定的时间后被重置。保证了之后的调用不会被直接拒绝。DegradeRule中passCheck方法部分代码:

      ......
      if (grade == RuleConstant.DEGRADE_GRADE_RT) {
            double rt = clusterNode.avgRt();
            if (rt < this.count) {
                passCount.set(0);
                return true;
            }

            // Sentinel will degrade the service only if count exceeds.
            if (passCount.incrementAndGet() < RT_MAX_EXCEED_N) {
                return true;
            }
        }
        .....
        if (cut.compareAndSet(false, true)) {
            ResetTask resetTask = new ResetTask(this);
            pool.schedule(resetTask, timeWindow, TimeUnit.SECONDS);
        }

在平均响应时间做参考时,到达阈值时并没有拒绝,而是任然可以通过RT_MAX_EXCEED_N数量的调用。resetTask主要是重置passCount和cut状态,他是被放在一个线程池中延迟启动线程。

• FlowSlot 流式处理槽
当qps,success,pass,block有这些数据时候,怎么通过这些数据去提供限流。当然check pass的依据是通过 FlowRule中规则限定的。基本上FlowRule中依据的规则主要是qps和thread方式做限制的。但是这些qps,succes等维度统计数据都记录在Node中,发现StatisticNode中相关的具体Node有好多形式,例如与Origin关联的Node,与当前文本关联的DefaultNode,还有与resource关联的ClusterNode。那么限流肯定会取其中一个Node做依据,所以FlowRule中一个属性strategy,来去选择某一个Node。

 /**
     * Flow control strategy based on invocation chain.
     *
     * {@link RuleConstant#STRATEGY_DIRECT} for direct flow control (by origin);
     * {@link RuleConstant#STRATEGY_RELATE} for relevant flow control (with relevant resource);
     * {@link RuleConstant#STRATEGY_CHAIN} for chain flow control (by entrance resource).
     */
    private int strategy = RuleConstant.STRATEGY_DIRECT;

从注释中,可以解读:
STRATEGY_DIRECT 直接通过origin相关的Node做控制;
STRATEGY_RELATE 通过refResource属性获取Node做关联控制;
STRATEGY_CHAIN 就是通过入口的Node进行控制;
至于如何代码实现逻辑可以通过 FlowRuleChecker类中静态方法selectNodeByRequesterAndStrategy()得到Node。

总结:

sentinel如何限流基本可以略知一二,主要在调用时,需要声明资源名字,对该资源依附自身的StatisticNode,经过一些列的ProcessorSlot的entry,包括系统限定,降级限定,还有流式限定等等。如果无法通过时,Block异常,那么就不会真正执行业务方法,保护了资源对应的系统功能,间接保证服务的稳定。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容