sentinal源码1-入口

  • sentinal是阿里开源的限流降级的一个基础组件,本系列主要是记录本人对sentinal源码的理解。
    sentinal官网
  • 节点关系图


    image.png

一 Context

1.1 ContextUtil类属性

public class ContextUtil {
    private static ThreadLocal<Context> contextHolder = new ThreadLocal<>();

    private static volatile Map<String, DefaultNode> contextNameNodeMap = new HashMap<>();

    private static final ReentrantLock LOCK = new ReentrantLock();
    private static final Context NULL_CONTEXT = new NullContext();
...
}
  • 线程context上下文信息,使用static ThreadLocal<Context> contextHolder存储,每个线程一个
  • contextNameNodeMap以context名为key存储conext的入口EntranceNode类型的节点,多个线程使用相同名字时,用的同一个节点
  • 使用ReentrantLock LOCK可重入锁保证context入口节点的并发读写

1.2 静态初始化

static {
    // Cache the entrance node for default context.
    initDefaultContext();
}

private static void initDefaultContext() {
    String defaultContextName = Constants.CONTEXT_DEFAULT_NAME;
    EntranceNode node = new EntranceNode(new StringResourceWrapper(defaultContextName, EntryType.IN), null);
    Constants.ROOT.addChild(node);
    contextNameNodeMap.put(defaultContextName, node);
}
  • 使用static在类加载时初始化
  • 初始化默认的context入口节点,名字为sentinel_default_context,未指定context名称时统一使用该节点
  • 设置为ROOT节点的子节点

1.3 ROOT节点初始化

public final class Constants {

    public static final String SENTINEL_VERSION = VersionUtil.getVersion("1.5.1");

    public final static int MAX_CONTEXT_NAME_SIZE = 2000;
    public final static int MAX_SLOT_CHAIN_SIZE = 6000;

    public final static String ROOT_ID = "machine-root";
    public final static String CONTEXT_DEFAULT_NAME = "sentinel_default_context";

    public final static String TOTAL_IN_RESOURCE_NAME = "__total_inbound_traffic__";

    public final static DefaultNode ROOT = new EntranceNode(new StringResourceWrapper(ROOT_ID, EntryType.IN),
        Env.nodeBuilder.buildClusterNode());

    /**
     * Statistics for {@link SystemRule} checking.
     */
    public final static ClusterNode ENTRY_NODE = new ClusterNode();

    /**
     * Response time that exceeds TIME_DROP_VALVE will be calculated as TIME_DROP_VALVE.
     * Default value is 4900 ms
     * It can be configured by property file or JVM parameter -Dcsp.sentinel.statistic.max.rt=xxx
     * See {@link SentinelConfig#statisticMaxRt()}
     */
    public static int TIME_DROP_VALVE = SentinelConfig.statisticMaxRt();

    /**
     * The global switch for Sentinel.
     */
    public static volatile boolean ON = true;
}
  • Constant类型提供一些常量数据
  • 通知提供一些初始化操作,如初始化一个machine-root名字的根节点,是所有context入口节点的父节点

1.4 获取指定名称的context

  • 接口函数,参数为context名称和调用来源origin
public static Context enter(String name, String origin) {
    if (Constants.CONTEXT_DEFAULT_NAME.equals(name)) {
        throw new ContextNameDefineException(
            "The " + Constants.CONTEXT_DEFAULT_NAME + " can't be permit to defined!");
    }
    return trueEnter(name, origin);
}

1.4.1 初始化线程的context

  • 线程变量存储,一个线程一个
  • context node节点按名字唯一创建,多个线程的context可以共用node
  • 限制context node的最大数量为2000
  • 加锁,二次检查conext node是否存在,不存在则新建EntranceNode类型node
  • 设置为ROOT节点的子节点,更新conext node存储map数据
  • 初始化context
  • 设置context的origin值,调用来源
  • 保存线程变量Threadlocal,返回context
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 {
                            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 = new Context(node, name);
        context.setOrigin(origin);
        contextHolder.set(context);
    }

    return context;
}

1.5 context结构

public class Context {
    private final String name;//context名称
    private DefaultNode entranceNode;//context名字对应的入口节点,相同名称共用一个节点
    private Entry curEntry;//当前资源请求处理节点
    private String origin = "";//资源请求来源
    private final boolean async;//异步处理标记
}

二 资源请求入口

  • 两种类型的入口
    SphU 请求失败抛异常,则正常处理
    SphO 请求成功返回true,请求失败返回false
  • 实际调用的都是Env.sph.enter()函数,请求资源失败会抛异常。
    SphO是对异常进行catch,返回false。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • ReentrantLock 介绍 一个可重入的互斥锁,它具有与使用{synchronized}方法和语句访问的隐式...
    tomas家的小拨浪鼓阅读 4,108评论 1 4
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,142评论 1 32
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,921评论 0 5
  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,288评论 0 16
  • 书名:《如何高效读懂一本书》 作者:秋叶 金句: 读书是易事,思索是难事,但两者缺一,便全无用处。 ——富兰克林 ...
    预约晴天阅读 711评论 0 3