Summary of slot.

In Sentinel, all resources are assigned a resourceName (resourceName). Each resource call creates an Entry object. Entry can be created automatically by adapting to the mainstream framework or explicitly by annotating or calling the SphU API. When an Entry is created, a series of slot chains are also created. These slots have different responsibilities, for example:

  • NodeSelectorSlotTo be responsible for thePath for collecting resourcesAnd put these resource invocation paths into a tree structurestorageUp, used according to the call pathCurrent limiting the drop;
  • ClusterBuilderSlotIs used tostorageresourcesstatisticsAs well asCaller information, such as the resource’s RT, QPS, thread count, and so onAs the basis of multi-dimensional flow limiting and degradation;
  • StatisticSlotIs used torecord, statistics of different latitudesRuntime indicator monitoring information;
  • FlowSlotIs used according to preset traffic limiting rules and the status of the previous slot statisticsFlow control;
  • AuthoritySlotDo so based on the configured blacklist and whitelist and call source informationWhitelist control;
  • DegradeSlotYou do it with statistics and preset rulesFusing the drop;
  • SystemSlotIs based on the system status, such as load1Control total inlet flow;

The following is a diagram of the relationship


Solt basic logic and code demonstration

After each Slot performs business logic processing, the fireEntry() method is called, which triggers the entry method of the next node, which calls its fireEntry method, and so on until the last Slot, thus forming the sentinel responsibility chain.

  • Workflow Overview:


    I’ll talk about the basic structure and usage of slot based on its basic implementation processorSlot


  • Take a look at the top-level interface ProcessorSlot

    public interface ProcessorSlot<T> {
        void entry(...).; // Start the entry
        void fireEntry(...).;// Finish means finished
        void exit(...).;// Exit the slot
        void fireExit(...).;// Exit the slot
    }
    Copy the code

This interface has four methods, Entry, fireEntry, exit, fireExit

  • AbstractLinkedProcessorSlot ProcessorSlot abstract implementation

    public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {
    
        privateAbstractLinkedProcessorSlot<? > next =null;
    
        @Override
        public void fireEntry(...). throws Throwable {
            // If there is another slot after the service is completed
            if(next ! =null) { next.transformEntry(context, resourceWrapper, obj, count, prioritized, args); }}@SuppressWarnings("unchecked")
        // An entry pointing to the next slot. Each slot has its own implementation depending on its responsibilities
        void transformEntry(...). throws Throwable {
            T t = (T)o;
            entry(context, resourceWrapper, t, count, prioritized, args);
        }
    
        @Override
        public void fireExit(...). {
            // After exit of a slot is executed, if there is another slot that is not closed
            if(next ! =null) {
                // Exit pointing to the next slotnext.exit(context, resourceWrapper, count, args); }}publicAbstractLinkedProcessorSlot<? > getNext() {return next;
        }
    
        public void setNext(AbstractLinkedProcessorSlot
              next) {
            this.next = next; }}Copy the code
    • DefaultProcessorSlotChain realized the chain (setNext and getNext)

      public class DefaultProcessorSlotChain extends ProcessorSlotChain {
      	/ / directly realized AbstractLinkedProcessorSlot instance and as the first, can be understood as the current slotAbstractLinkedProcessorSlot<? > first =new AbstractLinkedProcessorSlot<Object>() {
      
              @Override
              public void entry(...).
                  throws Throwable {
                  super.fireEntry(context, resourceWrapper, t, count, prioritized, args);
              }
      
              @Override
              public void exit(...). {
                  super.fireExit(context, resourceWrapper, count, args); }};// The default slot is end.AbstractLinkedProcessorSlot<? > end = first;@Override
          public void addFirst(AbstractLinkedProcessorSlot
                  protocolProcessor) {
              protocolProcessor.setNext(first.getNext());
              first.setNext(protocolProcessor);
              // If the current is the last one
              if(end == first) { end = protocolProcessor; }}@Override
          public void addLast(AbstractLinkedProcessorSlot
                  protocolProcessor) {
              // Place the latter slot next to the current slot
              end.setNext(protocolProcessor);
              // Point end to the latter slotend = protocolProcessor; }}Copy the code
  • Examples of AbstractLinkedProcessorSlot DemoSlot:

    public class DemoSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
    
        // Start the entry
        @Override
        public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
                throws Throwable {
            // Finish means finished
            fireEntry(context, resourceWrapper, node, count, prioritized, args);
        }
        // Exit the slot
        @Override
        public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
            // Exit the slotfireExit(context, resourceWrapper, count, args); }}Copy the code

Here we have looked at the basic implementation of Slot to summarize:

  • 1. Initializationfirstandendtheslot.
  • 2. Start the operationentry
  • 3. Start the operationfireEntryAnd query whether the nextslotIf yes, go to Step 2
  • 4. Start the operationexit
  • 5. Start the operationfireExitAnd queries whether there is a next oneslotIf yes, go to Step 4
  • End of 6.

When we use the Slot approach, we need to implement a lifeCycle similar to Tomcat’s, but the difference is that Tomcat’s lifeCycle uses an asynchronous event to execute the in-container logic, whereas Sentinel uses a chain call with a parent dependency, with an emphasis on sequential execution.

By default, the order between slots is fixed, because some slots rely on the results calculated by other slots to work.

Now let’s see how do we guarantee order

SLOT of the load

1. Define the order

Sentinel annotates the sequential parameters on each instantiated slot, such as

@SpiOrder(-10000)
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {
Copy the code

This is a custom annotation that saves mostly the above (-10000) as the order weight

2. The SPI load

The default chain will invoke the sentinel class loading tools SpiLoader loadPrototypeInstanceListSorted (ProcessorSlot. Class);

This method loads all classes that implement ProcessorSlot as SPI

@SpiOrder(-10000)
public class NodeSelectorSlot 
@SpiOrder(9000)public class ClusterBuilderSlot
@SpiOrder(8000)public class LogSlot
@SpiOrder(7000)public class StatisticSlot
@SpiOrder(5000)public class SystemSlot
@SpiOrder(6000)public class AuthoritySlot
@SpiOrder(2000)public class FlowSlot
@SpiOrder(1000)public class DegradeSlot
Copy the code

3. Sort after loading

 public static <T> List<T> loadPrototypeInstanceListSorted(Class<T> clazz) {
	// This is the loading of step 2
    ServiceLoader<T> serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz);

    List<SpiOrderWrapper<T>> orderWrappers = new ArrayList<>();
    // loop over the load
    for (T spi : serviceLoader) {
        // Query the order of corresponding classes (step 1)
        int order = SpiOrderResolver.resolveOrder(spi);
        // Insert order and class into List (manually ordered array)SpiOrderResolver.insertSorted(orderWrappers, spi, order); }}// The sorting method is simple
private static <T> void insertSorted(List<SpiOrderWrapper<T>> list, T spi, int order) {
    int idx = 0;
    for (; idx < list.size(); idx++) {
        // Loop through the list of fixed length, once compare the size
        if (list.get(idx).getOrder() > order) {
            break; }} list.add(idx,new SpiOrderWrapper<>(order, spi));
}
Copy the code