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:
NodeSelectorSlot
To 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;ClusterBuilderSlot
Is 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;StatisticSlot
Is used torecord, statistics of different latitudesRuntime indicator monitoring information;FlowSlot
Is used according to preset traffic limiting rules and the status of the previous slot statisticsFlow control;AuthoritySlot
Do so based on the configured blacklist and whitelist and call source informationWhitelist control;DegradeSlot
You do it with statistics and preset rulesFusing the drop;SystemSlot
Is 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. Initialization
first
andend
theslot
. - 2. Start the operation
entry
- 3. Start the operation
fireEntry
And query whether the nextslot
If yes, go to Step 2 - 4. Start the operation
exit
- 5. Start the operation
fireExit
And queries whether there is a next oneslot
If 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