【sentinel】深入浅出之原理篇SlotChain

最近在负责做网关类系统,需要考虑做限流熔断功能,基于QPS,基于线程数,对于集群,单机做限流,熔断,而Sentinel能满足我的所有需求,配置接入方便,设计上很适合做扩展。
关于sentinel的详细介绍逅弈大佬已经有详细的博客介绍了,mark一下(https://www.jianshu.com/u/51121bddcd2a)。
我再简单摘要一下。

diagram.png

在整个限流熔断的过程中,实际是基于slotChain来实现,而 slotchain内部则是使用链表来实现。


public interface ProcessorSlot<T> {

    void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, boolean prioritized,
               Object... args) throws Throwable;
    void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized,
                   Object... args) throws Throwable;
    void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);

    void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
}

public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {

    private AbstractLinkedProcessorSlot<?> next = null;

    @Override
    public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
        throws Throwable {
        if (next != null) {
            next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);
        }
    }
    @SuppressWarnings("unchecked")
    void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)
        throws Throwable {
        T t = (T)o;
        entry(context, resourceWrapper, t, count, prioritized, args);
    }
    @Override
    public void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
        if (next != null) {
            next.exit(context, resourceWrapper, count, args);
        }
    }
    public AbstractLinkedProcessorSlot<?> getNext() {
        return next;
    }
    public void setNext(AbstractLinkedProcessorSlot<?> next) {
        this.next = next;
    }
}

可以看到AbstractLinkedProcessorSlot为一个Slot节点,通过setNext指定下一个Slot节点,通过 fireEntry()方法,调用下一个节点的transformEntry()最终调用到下一个Slot节点的entry方法,本身的结构类似于

clsss Slot {
  Slot next;
}

这不就是大学时数据结构常提到的链表结构。
再来看看 ProcessorSlotChainDefaultProcessorSlotChain

public abstract class ProcessorSlotChain extends AbstractLinkedProcessorSlot<Object> {
    //添加头节点
    public abstract void addFirst(AbstractLinkedProcessorSlot<?> protocolProcessor);
    //添加下一个节点
    public abstract void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor);
}

public class DefaultProcessorSlotChain extends ProcessorSlotChain {

    AbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {

        @Override
        public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)
            throws Throwable {
            super.fireEntry(context, resourceWrapper, t, count, prioritized, args);
        }

        @Override
        public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
            super.fireExit(context, resourceWrapper, count, args);
        }

    };
    AbstractLinkedProcessorSlot<?> end = first;

    @Override
    public void addFirst(AbstractLinkedProcessorSlot<?> protocolProcessor) {
        protocolProcessor.setNext(first.getNext());
        first.setNext(protocolProcessor);
        if (end == first) {
            end = protocolProcessor;
        }
    }

    @Override
    public void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {
        end.setNext(protocolProcessor);
        end = protocolProcessor;
    }


    @Override
    public void setNext(AbstractLinkedProcessorSlot<?> next) {
        addLast(next);
    }

    @Override
    public AbstractLinkedProcessorSlot<?> getNext() {
        return first.getNext();
    }

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)
        throws Throwable {
        first.transformEntry(context, resourceWrapper, t, count, prioritized, args);
    }

    @Override
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
        first.exit(context, resourceWrapper, count, args);
    }
}

可以看到, DefaultProcessorSlotChain实质是指定头节点FirstSlotendSnot的链表。就如下面结构 :

public Class Chain{
  Slot first;
  Slot end;
}

而在初始化 SlotChain的时候,实质添加顺序是这样子滴

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;
}

所以整个SlotChain的结构如下图:


DefaultProcessorSlotChain

而具体的熔断限流都是每个Slot的具体职责:

  • NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;
  • ClusterBuilderSlot则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;
  • StatistcSlot 则用于记录,统计不同纬度的 runtime 信息;
  • SystemSlot 则通过系统的状态,例如 load1 等,来控制总的入口流量;
  • AuthorizationSlot 则根据黑白名单,来做黑白名单控制;
  • FlowSlot则用于根据预设的限流规则,以及前面 slot 统计的状态,来进行限流;
  • DegradeSlot则通过统计信息,以及预设的规则,来做熔断降级;

可以看到,我需要的熔断,限流,白名单控制都可以通过配置AuthorizationSlotFlowSlotDegradeSlot实现。

推荐阅读更多精彩内容