• Sentinel笔记-核心类


    一些概念

    • 资源:资源是 Sentinel 的关键概念。资源,可以是一个方法、一段代码、由应用提供的接口,或者由应用调用其它应用的接口。
    • 规则:围绕资源的实时状态设定的规则,包括流量控制规则、熔断降级规则以及系统保护规则、自定义规则。
    • 降级:在流量剧增的情况下,为保证系统能够正常运行,根据资源的实时状态、访问流量以及系统负载有策略的拒绝掉一部分流量。

    Resource 

    Sentinel 中指标数据统计以资源为维度。资源使用 ResourceWrapper 对象表示,我们把 ResourceWrapper 对象称为资源 ID。如果一个资源描述的是一个接口,那么资源名称通常就是接口的 url,例如“GET:/v1/demo”。

    ResourceWrapper 有三个字段:

    • name 为资源名称,例如:“GET:/v1/demo”。
    • entryType 为流量类型,即流入流量还是流出流量,通俗点说就是发起请求还是接收请求。
    • resourceType 表示资源的类型,例如 Dubbo RPC、Web MVC 或者 API Gateway 网关。
    public abstract class ResourceWrapper {
        protected final String name;
        protected final EntryType entryType;
        protected final int resourceType;
        public ResourceWrapper(String name, EntryType entryType, int resourceType) {
            this.name = name;
            this.entryType = entryType;
            this.resourceType = resourceType;
        }
    }
    
    //entryType
    public enum EntryType {
        IN,
        OUT;
    }
    
    //resourceType
    public final class ResourceTypeConstants {
        public static final int COMMON = 0;
        public static final int COMMON_WEB = 1;
        public static final int COMMON_RPC = 2;
        public static final int COMMON_API_GATEWAY = 3;
        public static final int COMMON_DB_SQL = 4;
    }

    Node

    Node 用于持有实时统计的指标数据,Node 接口定义了一个 Node 类所需要提供的各项指标数据统计的相关功能,内部由具体滑动窗口实现。

    它的几个实现类:DefaultNode、ClusterNode、EntranceNode、StatisticNode 的关系如下图所示。

    StatisticNode

    Statistic 即统计的意思,StatisticNode 是 Node 接口的实现类,是实现实时指标数据统计 Node,包含秒级(可抢占OccupiableBucketLeapArray)和分钟级(BucketLeapArray)两个滑动窗口结构。

    StatisticNode 还负责统计并行占用的线程数,用于实现信号量隔离,按资源所能并发占用的最大线程数实现限流。当接收到一个请求就将 curThreadNum 自增 1,当处理完请求时就将 curThreadNum 自减一,如果同时处理 10 个请求,那么 curThreadNum 的值就为 10。

    //对外提供统一的metric操作接口
    public interface Node {
        long totalRequest(); // 获取总的请求数
        long totalPass(); // 获取通过的请求总数
        long totalSuccess(); // 获取成功的请求总数
        *****
        void increaseThreadNum(); // 自增占用线程
        void decreaseThreadNum(); // 自减占用线程
        void reset(); // 重置滑动窗口
    }
    
    public class StatisticNode implements Node {
        // 秒级滑动窗口,2 个时间窗口大小为 500 毫秒的 Bucket
        private  Metric rollingCounterInSecond = new ArrayMetric(2,1000);
    
        // 分钟级滑动窗口,60 个 Bucket 数组,每个 Bucket 统计的时间窗口大小为 1 秒
        private  Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false);
    
        // 统计并发使用的线程数
        private LongAdder curThreadNum = new LongAdder();
    
        //**具体操作由内部的ArrayMetric 滑动窗口来实现。
        @Override
        public void addPassRequest(int count) {
            rollingCounterInSecond.addPass(count);
            rollingCounterInMinute.addPass(count);
        }
    
        @Override
        public void addRtAndSuccess(long rt, int successCount) {
            rollingCounterInSecond.addSuccess(successCount);
            rollingCounterInSecond.addRT(rt);
    
            rollingCounterInMinute.addSuccess(successCount);
            rollingCounterInMinute.addRT(rt);
        }
        *************    
        @Override
        public void increaseThreadNum() {
            curThreadNum.increment();
        }
    
        @Override
        public void decreaseThreadNum() {
            curThreadNum.decrement();
        }    
    }

    ClusterNode

    Sentinel 使用 ClusterNode 统计每个资源全局的指标数据,以及统计该资源按调用来源区分的指标数据。全局数据指的是不区分调用链路,一个资源 ID(Resource) 只对应一个 ClusterNode。

    public class ClusterNode extends StatisticNode {
        // 资源名称 
          private final String name;
    
        // 资源类型
        private final int resourceType;
    
        // 来源指标数据统计,记录不同来源App的统计信息。
          private Map<String, StatisticNode> originCountMap = new HashMap<>();
    
    } 

    DefaultNode 

    DefaultNode 是实现以资源为维度的指标数据统计的 Node,是将资源 ID 和 StatisticNode 映射到一起的 Node,用于统计调用链路上某个资源的数据,维持树状结构。

    DefaultNode 是 StatisticNode 的子类,构造方法要求传入资源 ID,表示该 Node 用于统计哪个资源的实时指标数据,指标数据统计则由父类 StatisticNode 完成。

    public class DefaultNode extends StatisticNode {
    
        private ResourceWrapper id;
    
        private volatile Set<Node> childList = new HashSet<>();
      
        //每个resourceId 对应一个clusterNode  
        private ClusterNode clusterNode;
    }

    childList 是一个 Node(DefaultNode)集合,用于存放子节点.

    备注:在使用同一个 Context 的情况下,只会为一个资源创建一个 DefaultNode 

     

    EntranceNode

    1. 入口节点,特殊的链路节点,对应某个 Context 入口的所有调用数据。Constants.ROOT 节点也是入口节点。
    2. EntranceNode 是一个特殊的 Node,它继承 DefaultNode,用于维护一颗树,从根节点到每个叶子节点都是不同请求的调用链路,所经过的每个节点都对应着调用链路上被 Sentinel 保护的资源,一个请求调用链路上的节点顺序正是资源被访问的顺序。

    Demo:

    在一个 Web MVC 应用中,每个接口就是一个资源,Sentinel 通过 Spring MVC 拦截器拦截每个接口的入口,统一创建名为“sentinel_spring_web_context”的 Context,名称相同的 Context 都使用同一个 EntranceNode。一个 Web 应用可能有多个接口,而 childList 就用于存储每个接口对应的 DefaultNode。

    如果想统计一个应用的所有接口(不一定是所有,没有被调用过的接口不会创建对应的 DefaultNode)总的 QPS,只需要调用 EntranceNode 的 totalQps 就能获取到。EntranceNode 的 totalQps 方法代码如下:

    public class EntranceNode extends DefaultNode {
    
        public EntranceNode(ResourceWrapper id, ClusterNode clusterNode) {
            super(id, clusterNode);
        }
    
        @Override
        public double avgRt() {
            double total = 0;
            double totalQps = 0;
            for (Node node : getChildList()) {
                total += node.avgRt() * node.passQps();
                totalQps += node.passQps();
            }
            return total / (totalQps == 0 ? 1 : totalQps);
        }
        
        @Override
        public double totalQps() {
            double r = 0;
            for (Node node : getChildList()) {
                r += node.totalQps();
            }
            return r;
        }    
         ***
    }

    Sentinel 中的 Context 与 Entry

    Context

    Context 代表调用链路上下文,贯穿一次调用链路中的所有 Entry。Context 维持着入口节点(entranceNode)、本次调用链路的 curNode、调用来源(origin)等信息。Context 名称即为调用链路入口名称。

    Context 维持的方式:通过 ThreadLocal 传递,只有在入口 enter 的时候生效。由于 Context 是通过 ThreadLocal 传递的,因此对于异步调用链路,线程切换的时候会丢掉 Context,因此需要手动通过 ContextUtil.runOnContext(context, f) 来变换 context。

    //This class holds metadata of current invocation
    public class Context {
        private final String name;
        private DefaultNode entranceNode;
        private Entry curEntry;
        private String origin = "";
    }
    • name:Context 的名称。
    • entranceNode:当前调用树的入口节点,类型为 EntranceNode。同一个入口的资源,每个资源对应一个 DefaultNode,entranceNode#childList 用于存储这些资源的 DefaultNode。
    • curEntry:当前 Entry(CtEntry),用于操作当前的node节点
    • origin:调用来源的名称,即服务消费者的名称或者服务消费者的来源 IP,取决于服务消费者是否使用 Sentinel,由 Sentinel 适配层传递过来。例如:服务提供者是 Spring MVC 应用,且服务提供者使用 Sentinel 的 Web MVC 适配,那么 Sentinel 会尝试从请求头获取"S-user",如果服务消费者有在请求头传递这个参数,那么就能够获取到。

    Entry(CtEntry)

    1. 在调用 Context#getCurNode 方法获取调用链路上当前访问到的资源的 DefaultNode 时,实际是从 Context#curEntry 获取的,Entry 维护了当前资源的 DefaultNode,以及调用来源的 StatisticNode。
    2. 每一次资源调用都会创建一个 EntryEntry 包含了资源名、curNode(当前统计节点)、originNode(来源统计节点)等信息。
    3. CtEntry 为普通的 Entry,在调用 SphU.entry(xxx) 的时候创建。特性:Linked entry within current context(内部维护着 parent 和 child
    4. 需要注意的一点:CtEntry 构造函数中会做调用链的变换,即将当前 Entry 接到传入 Context 的调用链路上(setUpEntryFor)。
    5. 资源调用结束时需要 entry.exit()。exit 操作会过一遍 slot chain exit,恢复调用栈,exit context 然后清空 entry 中的 context 防止重复调用。
    //com.alibaba.csp.sentinel.context.Context#getCurNode
    public Node getCurNode() {
        return curEntry == null ? null : curEntry.getCurNode();
    }
    
    public abstract class Entry implements AutoCloseable {
        private static final Object[] OBJECTS0 = new Object[0];
        private long createTime;
        // 当前节点(DefaultNode)
        private Node curNode;
        // 来源节点
        private Node originNode;
        private Throwable error;
        // 资源
        protected ResourceWrapper resourceWrapper;
    }
    class CtEntry extends Entry { // 当前 Entry 指向的父 Entry protected Entry parent = null; // 父 Entry 指向当前 Entry protected Entry child = null; // 当前资源的 ProcessorSlotChain protected ProcessorSlot<Object> chain; // 当前上下文 protected Context context; }

    CtEntry 用于维护父子 Entry,每一次调用 SphU#entry 方法都会创建一个 CtEntry。如果服务 B 在处理一个请求的路径上会多次调用 SphU#entry,那么这些 CtEntry 会构成一个双向链表。在每次创建 CtEntry,都会将 Context.curEntry 设置为这个新的 CtEntry,双向链表的作用就是在调用 CtEntry#exit 方法时,能够将 Context.curEntry 还原为上一个资源的 CtEntry。

    几种 Node 的维度(数目):

    • ClusterNode 的维度是 resource
    • DefaultNode 的维度是 resource * context,存在每个 NodeSelectorSlot 的 map 里面
    • EntranceNode 的维度是 context,存在 ContextUtil 类的 contextNameNodeMap 里面
    • 来源节点(类型为 StatisticNode)的维度是 resource * origin,存在每个 ClusterNode 的 originCountMap 里面
  • 相关阅读:
    一些常用的代码评审工具
    Atlassian旗下一干team build软件
    Jira功能全介绍
    项目经验分享
    网址、下载地址
    Java 字节码解读
    Gitlab 安装
    博客园设置
    mybatis 遇到空串无法判断
    Shell 脚本入门
  • 原文地址:https://www.cnblogs.com/snow-man/p/15499103.html
Copyright © 2020-2023  润新知