美文网首页服务监控和治理
Sentinel原理之重要类介绍

Sentinel原理之重要类介绍

作者: 九点半的马拉 | 来源:发表于2020-04-20 22:47 被阅读0次

    该篇是分析Sentinel原理的第一篇文章。由于接触该中间件的时间较短,可能会出现一些理解偏差。希望大家积极交流改正。文章主要内容主要是阅读了一些大神的文章,并加上自己的理解写的。

    主要参考的文章是在Sentinel的github中找到的,具体地址如下: 参考文章

    Sentinel的主要功能就是流量控制和熔断降级。具体概念在之前写的有关dubbo的限流和熔断中已介绍,这里就不再赘述了。

    先简单举一个如何使用的例子。

    public static void main(String[] args) {
        try {
            Context context=ContextUtil.enter("context1");
            Entry entry=SphU.entry("HelloWorld");
            entry.exit();
            ContextUtil.exit();
        } catch (BlockException ex) {
            // 处理被流控的逻辑
            System.out.println("blocked!");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
    try (Entry entry = SphU.entry("HelloWorld")) {
        // Your business logic here.
        System.out.println("hello world");
    } catch (BlockException e) {
        // Handle rejected request.
        e.printStackTrace();
    }
    

    上面是两个典型的流量控制例子。读者会发现有一处很明显的不同,即Context类,它用来表示一个调用的上下文,实际不加也影响,具体的在后面分析。

    例子中还有一个重要的类Entry,里面的参数HelloWorld可以看做一个资源名,在具体使用之前,我们需要自己定义一些规则。 可以使用下面的方法定义一个关于流量控制的规则:

    private void initFlowQpsRule() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule(resourceName);
        // set limit qps to 20
        rule.setCount(20);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setLimitApp("default");
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
    

    当调用SphU.entry("HelloWorld")时,如果没有报异常,说明可以执行自己的业务代码;如果报错,说明当前不符合条件,拒绝执行后面的业务代码。

    几个核心概念

    Resource

    资源是Sentinel中的一个核心概念,它可以是应用程序提供的服务,也可以是应用程序调用的其他服务,也可以是一段代码。当某个服务的请求非常多,经常被陡增的流量冲垮,造成性能降低或者不可用,这时我们可以定义一个Sentinel的资源,通过该资源对请求进行调整,进行流量控制和服务降级。

    在Sentinel中表示具体资源的类是ResourceWrapper

    public abstract class ResourceWrapper {
        # 资源名
        protected final String name;
        # 是入站还是出站
        protected final EntryType entryType;
        # 资源类型
        protected final int resourceType;
    

    Entry

    entry是sentinel中用来表示是否通过限流的一个凭证,如果能正常返回,则说明你可以访问被sentinel保护的后方服务,否则sentinel会抛出一个BlockException。另外,它保存了本次执行entry()方法的一些基本信息。每一次资源调用都会创建一个Entry

    public abstract class Entry implements AutoCloseable {
        # 当前Entry的创建时间,主要用来后期计算rt
        private long createTime;
        # 当前Entry所关联的node,该node主要是记录了当前context下该资源的统计信息
        private Node curNode;
        # 当前Entry的调用来源,通常是调用方的应用名称
        private Node originNode;
        private Throwable error;
        # 当前Entry所关联的资源
        protected ResourceWrapper resourceWrapper;
    
    class CtEntry extends Entry {
        protected Entry parent = null;
        protected Entry child = null;
        protected ProcessorSlot<Object> chain;
        protected Context context;
    

    可能这样介绍有些抽象,我借鉴一个别人的例子,很形象。



    从图中我们可以看出有一个user-center --> getUserInfo --> getOrderInfo的调用链路。根据代码,我们知道user-center是一个上下文名称,getUserInfogetOrderInfo是两个资源名称。

    上面例子中在一个Entry中又调用了一个Entry,存在链路,当在一个上下文中多次调用Sphu.entry()方法,就会创建一个调用树,这个树的节点之间是通过parent和child关系维持的。

    Node

    node中保存了资源的实时统计数据,例如:passQps,blockQps,rt等实时数据。

    node是一个接口,它有一个实现类StatisticNode,但是StatisticNode本身也有两个子类,一个是DefaultNode,另一个是ClusterNodeDefaultNode又有一个子类叫EntranceNode

    EntranceNode是每个上下文的入口,该节点是直接挂在root下,是全局唯一的,每一个context都有一个对应的entranceNodeDefaultNode是记录当前实时数据的,每个DefaultNode都关联着一个资源和ClusterNode。有着相同资源的defaultNode,它们关联着同一个clusterNode。

    public interface Node extends OccupySupport, DebugSupport {
        long totalRequest();
        long totalPass();
        long totalSuccess();
    
    public class DefaultNode extends StatisticNode {
        private ResourceWrapper id;
        private volatile Set<Node> childList = new HashSet<>();
        private ClusterNode clusterNode;
    

    Context

    public class Context {
        //上下文名称
        private final String name;
        //当前调用链的入口节点
        private DefaultNode entranceNode;
       //调用链中当前正在处理的entry
        private Entry curEntry;
        //此上下文的来源(通常表示不同的调用方,例如服务使用者名称或来源IP)
        private String origin = "";
    

    Context代表调用链路上下文,贯穿一次调用链路中的所有Entry
    每次调用SphU.entry()都需要在一个context中执行,如果没有当前执行时还没有context,那么框架会使用默认的context,即sentinel_default_context

    Context是保存在ThreadLocal中的,每次执行的时候会优先到ThreadLocal中获取。如果Context为null才会再次去创建一个context。

    Entry执行exit方法时,当当前的entry的parent为null时,说明当前entry是最上层的节点,该上下文的entry已执行完,所以在此时需要将context设置为null,从ThreadLocal中清除。

    以上就是比较重要的实体类介绍。

    参考文章:
    Sentinel核心类解析
    Sentinel原理-实体类
    阿里Sentinel源码解析

    相关文章

      网友评论

        本文标题:Sentinel原理之重要类介绍

        本文链接:https://www.haomeiwen.com/subject/zggsihtx.html