sentinal源码2-初始化

  • 包含很多static的初始化块,并通过ServiceLoader的方式加载各种类

一 static初始化块

static初始化块.png

二 CtSph

  • 提供资源请求entry初始化函数

2.1 同步context

  • 1 超过可限流的context入口类型数,则不做限流
  • 2 未指定则使用默认名称的context入口节点
  • 3 全局限流开关Constants.ON,关闭则不做限流
  • 4 获取当前资源对应的处理链,每个资源对应一个处理链,名称活方法表示资源
  • 5 资源对应的资源节点处理链数达到上限,则后续资源请求不做处理
  • 6 实例化资源请求Entry,按资源节点处理链依次处理,进行统计,资源申请等操作
  • 7 资源申请失败,按处理链做失败统计等处理
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
    throws BlockException {
    Context context = ContextUtil.getContext();
    if (context instanceof NullContext) { //1
        return new CtEntry(resourceWrapper, null, context);
    }

    if (context == null) {//2
        context = MyContextUtil.myEnter(Constants.CONTEXT_DEFAULT_NAME, "", resourceWrapper.getType());
    }

    if (!Constants.ON) {//3
        return new CtEntry(resourceWrapper, null, context);
    }

    ProcessorSlot chain = lookProcessChain(resourceWrapper);//4

    if (chain == null) {//5
        return new CtEntry(resourceWrapper, null, context);
    }
  
    Entry e = new CtEntry(resourceWrapper, chain, context);
    try {
        chain.entry(context, resourceWrapper, null, count, prioritized, args);//6
    } catch (BlockException e1) {
        e.exit(count, args);//7
        throw e1;
    } catch (Throwable e1) {
        RecordLog.info("Sentinel unexpected exception", e1);
    }
    return e;
}

2.1.1 lookProcessChain

  • 根据context名称获取处理链
  • 使用hashmap存储
    private static volatile Map chainMap = new HashMap();
  • 二次加锁校验方式初始化
  • 写拷贝更新的方式更新存储map
ProcessorSlot lookProcessChain(ResourceWrapper resourceWrapper) {
    ProcessorSlotChain chain = chainMap.get(resourceWrapper);
    if (chain == null) {
        synchronized (LOCK) {
            chain = chainMap.get(resourceWrapper);
            if (chain == null) {
                // Entry size limit.
                if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
                    return null;
                }

                chain = SlotChainProvider.newSlotChain();
                Map newMap = new HashMap(
                    chainMap.size() + 1);
                newMap.putAll(chainMap);
                newMap.put(resourceWrapper, chain);
                chainMap = newMap;
            }
        }
    }
    return chain;
}

2.1.2 获取资源节点处理链

public ProcessorSlotChain build() {
    ProcessorSlotChain chain = new DefaultProcessorSlotChain();
    chain.addLast(new NodeSelectorSlot());
    chain.addLast(new ClusterBuilderSlot());
    chain.addLast(new LogSlot());
    chain.addLast(new StatisticSlot());
    chain.addLast(new SystemSlot());
    chain.addLast(new AuthoritySlot());
    chain.addLast(new FlowSlot());
    chain.addLast(new DegradeSlot());

    return chain;
}

2.2 异步context

//todo

三 Entry

3.1 类结构图

entry类.png

3.2 CtEntry

3.2.1 实例化

  • Entry实例化
  • 保存资源处理链,资源处理上下文context
CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot chain, Context context) {
    super(resourceWrapper);
    this.chain = chain;
    this.context = context;

    setUpEntryFor(context);
}

  • 设置Entry父子关系,更新context.curEntry为本次新建的Entry
private void setUpEntryFor(Context context) {
    // The entry should not be associated to NullContext.
    if (context instanceof NullContext) {
        return;
    }
    this.parent = context.getCurEntry();
    if (parent != null) {
        ((CtEntry)parent).child = this;
    }
    context.setCurEntry(this);
}
  • 保存对应的context名称
  • 保存资源请求时间
public Entry(ResourceWrapper resourceWrapper) {
    this.resourceWrapper = resourceWrapper;
    this.createTime = TimeUtil.currentTimeMillis();
}

3.2.2 资源请求失败处理

  • 1 失败的entry非context当前entry,则按entry父子关系依次向上调用所有entry的失败处理函数exit()
  • 2 失败entry是context的当前entry,
    调用资源处理链的exit函数
    更新context的curEntry为请求失败Entry的父节点Entry
    删除Entry的父子关系
    如Entry无父节点,则若是默认context则需要自动清理,手动配置的context则由用户手动退出。
    清理Entry的context属性
protected void exitForContext(Context context, int count, Object... args) throws ErrorEntryFreeException {
    if (context != null) {
        // Null context should exit without clean-up.
        if (context instanceof NullContext) {
            return;
        }
        if (context.getCurEntry() != this) {//1
            String curEntryNameInContext = context.getCurEntry() == null ? null : context.getCurEntry().getResourceWrapper().getName();
            // Clean previous call stack.
            CtEntry e = (CtEntry)context.getCurEntry();
            while (e != null) {
                e.exit(count, args);
                e = (CtEntry)e.parent;
            }
            String errorMessage = String.format("The order of entry exit can't be paired with the order of entry"
                + ", current entry in context: <%s>, but expected: <%s>", curEntryNameInContext, resourceWrapper.getName());
            throw new ErrorEntryFreeException(errorMessage);
        } else {//2
            if (chain != null) {
                chain.exit(context, resourceWrapper, count, args);
            }
            context.setCurEntry(parent);
            if (parent != null) {
                ((CtEntry)parent).child = null;
            }
            if (parent == null) {
                if (ContextUtil.isDefaultContext(context)) {
                    ContextUtil.exit();
                }
            }
            clearEntryContext();
        }
    }
}
  • 清理线程变量缓存的context
public static void exit() {
    Context context = contextHolder.get();
    if (context != null && context.getCurEntry() == null) {
        contextHolder.set(null);
    }
}

3.3 AsyncEntry

//todo

四 节点关系图

image.png

你可能感兴趣的:(sentinal源码2-初始化)