sentinal源码1-入口

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


    image.png

一 Context

1.1 ContextUtil类属性

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

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

    private static final ReentrantLock LOCK = new ReentrantLock();
    private static final Context NULL_CONTEXT = new NullContext();
...
}
  • 线程context上下文信息,使用static ThreadLocal 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 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 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。

你可能感兴趣的:(sentinal源码1-入口)