当前位置:网站首页>Alibaba Sentinel - workflow and principle analysis
Alibaba Sentinel - workflow and principle analysis
2022-07-29 03:09:00 【Ordinary people zzz~】
Sentinel - Workflow and principle analysis
With the popularity of microservices , Stability between services and services is becoming more and more important .Sentinel Distributed oriented 、 Traffic management component of multilingual isomerization service architecture , The main pointcut is traffic , Route from traffic 、 flow control 、 Traffic shaping 、 Fusing the drop 、 System adaptive overload protection 、 Hotspot traffic protection and other dimensions help developers ensure the stability of microservices .
One 、Sentinel Basic concepts
resources (Resource)
Resources are Sentinel Key concepts of . It can be Java Anything in the application , for example , Services provided by applications , Or services provided by other applications called by the application , It could even be a piece of code .
The rules (Rule)
Rules around the real-time state of resources , It can include flow control rules 、 Fusing degradation rules and system protection rules . All rules can be adjusted dynamically and in real time .Sentinel There are currently five types of rules :1. Flow control rules (FlowRule);2. Fusing degradation rules (DegradeRule);3. System protection rules (SystemRule);4. Source access control rules (AuthorityRule);5. Hot spot parameter rules (ParamFlowRule);
slot (Slot Chain)
Sentinel The workflow of the is expanded around the slot chain composed of slots . Every Slot All have their own functions , Through a certain sequence of arrangement , To achieve the ultimate purpose of current limiting and degradation .
Sentinel The built-in slot has NodeSelectorSlot、ClusterBuilderSlot、StatisticsSlot、ParamFlowSlot、SystemSlot、AuthoritySlot、FlowSlot、DegradeSlot. Of course, it also supports custom slots ,Sentinel take com.alibaba.csp.sentinel.slotchain.ProcessorSlot As SPI Interface (1.7.2 Version before com.alibaba.csp.sentinel.slotchain.SlotChainBuilder As SPI), bring Slot Chain With the ability to expand .
Call link context (Context)
Context Throughout all of the links in a call Entry.Context Maintain the metadata of the current call chain : Entry node 、 This time, call the link node 、 Call source and other information , adopt ThreadLocal Pass on .
EntryEntry Refer to Sentinel Whether the request passes a certificate of flow restriction . Every time you execute SphU.entry() or SphO.entry() Will return to one Entry To the caller . At the end of the resource call, you need entry.exit().Entry Contains the resource name 、curNode( Current statistics node )、originNode( Source statistics node ) Etc .
node (Node)
node . stay Sentinel The objects that save statistical data in 4 Kind of :1. StatisticNode: Statistics node , The most basic statistical node , It contains two sliding window structures of second level and minute level .2. DefaultNode: Link nodes , It is used to count the data of a resource on the call link , Maintain the tree structure .3. ClusterNode: Cluster node , It is used to count the global data of each resource ( Call links are not distinguished ), And storing the call data differentiated by source of the resource .4. EntranceNode: Entry node , Special link nodes , Corresponding to a certain Context All call data of the entry .
Two 、Sentinel Workflow
stay Sentinel Inside , All resources correspond to an asset name and a Entry. Entry It can be automatically created by adapting to the mainstream framework , It can also be annotated or called API According to create ; every last Entry At the time of creation , It will also create a series Function slot (slot chain), These slots have different responsibilities , Build through the responsibility chain , The overall framework is as follows :

these slot (slot chain) Have different responsibilities .
- NodeSelectorSlot: The path responsible for collecting resources , And the call path of these resources , Store in a tree structure , Used to limit the flow according to the call path 、 Downgrade .
- ClusterBuilderSlot: Statistics used to store assets , And caller information , for example , Of this resource RT( Mean response time )、QPS、Thread Count wait , This information will be used as a multi-dimensional limit 、 The basis of the downgrade .
- StatisticsSlot: Used to record 、 Statistics of different dimensions of runtime Indicator monitoring information .
- ParamFlowSlot: Used for resource configuration hotspot parameters 、 Current limiting rules and the previous slot The state of Statistics , To control the flow .
- SystemSlot: Through the state of the system , for example load1 etc. , To control the total inlet flow .
- AuthoritySlot: According to the configured black and white list and call source information , To do black and white list control .
- FlowSlot: It is used according to the preset current limiting rules and the front slot The state of Statistics , To control the flow .
- DegradeSlot: Through statistics and preset rules , To do the fusing degradation .
Custom slot (slot chain)
Sentinel take com.alibaba.csp.sentinel.slotchain.ProcessorSlot As SPI Interface (1.7.2 Version before com.alibaba.csp.sentinel.slotchain.SlotChainBuilder As SPI), bring Slot Chain With the ability to expand .
You can add your own custom slot And choreograph slot The order between , So that we can give Sentinel Add custom features .

The order of the default processor slots
// NodeSelectorSlot
public static final int ORDER_NODE_SELECTOR_SLOT = -10000;
// ClusterBuilderSlot
public static final int ORDER_CLUSTER_BUILDER_SLOT = -9000;
// LogSlot
public static final int ORDER_LOG_SLOT = -8000;
// StatisticsSlot
public static final int ORDER_STATISTIC_SLOT = -7000;
// AuthoritySlot
public static final int ORDER_AUTHORITY_SLOT = -6000;
// SystemSlot
public static final int ORDER_SYSTEM_SLOT = -5000;
// FlowSlot
public static final int ORDER_FLOW_SLOT = -2000;
// DegradeSlot
public static final int ORDER_DEGRADE_SLOT = -1000;
3、 ... and 、Sentinel Principle analysis
Use Sentinel To protect resources , It is mainly divided into several steps :
- Define the rules
- Define resources
- Check if the rules are in effect
3.1 Define the rules
Sentinel All rules of can be dynamically queried and modified in memory state , The amendment takes effect immediately after . meanwhile Sentinel It also provides related API, For you to customize your own rules and Strategies .
Sentinel The following rules are supported : Flow control rules 、 Fusing degradation rules 、 System protection rules 、 Source access control rules and Hot spot parameter rules .
Let's take the flow control rule as an example .
private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource(resource);
// Set max qps to 20
rule1.setCount(20);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
// load Flow control rules
FlowRuleManager.loadRules(rules);
}
You can see , In the process of initializing rules , We mainly created a FlowRule object , And then through FlowRuleManager Of loadRules(List<FlowRule> rules) Method loading rules .
FlowRuleManager.loadRules(rules)
public class FlowRuleManager {
// Rules for storing resources : A resource can have multiple flow restriction rules
// key -> resource name value -> After the heavy lifting FlowRule
private static volatile Map<String, List<FlowRule>> flowRules = new HashMap<>();
// Traffic rule listener : Dynamic rule implementation , Monitoring rule changes
private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
// Dynamic rule object : Store all FlowRule, When there is a change in the rules , notice FlowRuleManager Medium LISTENER To deal with
private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();
private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
new NamedThreadFactory("sentinel-metrics-record-task", true));
static {
// add to LISTENER To DynamicSentinelProperty in
currentProperty.addListener(LISTENER);
// Start a task : Every once in a while , Count and save the current limit information to the local disk
startMetricTimerListener();
}
/** * Load the rules */
public static void loadRules(List<FlowRule> rules) {
// update rule
currentProperty.updateValue(rules);
}
}
1. DynamicSentinelProperty.updateValue(T newValue)
DynamicSentinelProperty Medium boolean updateValue(T newValue) Used to refresh the current limit rules .
public class DynamicSentinelProperty<T> implements SentinelProperty<T> {
@Override
public boolean updateValue(T newValue) {
// Judge whether the existing rule is relative to the current rule
if (isEqual(value, newValue)) {
return false;
}
RecordLog.info("[DynamicSentinelProperty] Config will be updated to: {}", newValue);
// Update settings
value = newValue;
// notice listener Rule update
for (PropertyListener<T> listener : listeners) {
listener.configUpdate(newValue);
}
return true;
}
}
The main logic is to update rules , Then inform one of them Listener Make configuration updates .DynamicSentinelProperty Medium listener The object is FlowRuleManager Set the value in , So we call FlowRuleManager Medium FlowPropertyListener Methods of inner class .
2. FlowPropertyListener
FlowPropertyListener For monitoring Sentinel Dynamic changes of rules , Complete the local flow restriction rule cache update .
public class FlowRuleManager {
// Rules for storing resources : A resource can have multiple flow restriction rules
// key -> resource name value -> After the heavy lifting FlowRule
private static volatile Map<String, List<FlowRule>> flowRules = new HashMap<>();
private static final class FlowPropertyListener implements PropertyListener<List<FlowRule>> {
@Override
public synchronized void configUpdate(List<FlowRule> value) {
/******** Rule configuration update *********/
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
if (rules != null) {
flowRules = rules;
}
RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
}
@Override
public synchronized void configLoad(List<FlowRule> conf) {
/******** Rule configuration loading *********/
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);
if (rules != null) {
flowRules = rules;
}
RecordLog.info("[FlowRuleManager] Flow rules loaded: {}", rules);
}
}
}
among configUpdate(List<FlowRule> value) as well as configLoad(List<FlowRule> conf) Methods are called FlowRuleUtil.buildFlowRuleMap(List<FlowRule> list).
Be careful :configLoad(List<FlowRule> conf) as well as FlowRuleUtil.buildFlowRuleMap(List<FlowRule> list) Methods are added in front of methods synchronized keyword , So all internal operations are Thread safety Of .
3. FlowRuleUtil.buildFlowRuleMap(List list)
FlowRuleUtil.buildFlowRuleMap(List<FlowRule> list) For from List<FlowRule> list Build flow restriction rule mapping , By resource name (resource name) grouping .
public final class FlowRuleUtil {
public static Map<String, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list) {
return buildFlowRuleMap(list, null);
}
public static Map<String, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Predicate<FlowRule> filter) {
return buildFlowRuleMap(list, filter, true);
}
public static Map<String, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Predicate<FlowRule> filter,
boolean shouldSort) {
return buildFlowRuleMap(list, extractResource, filter, shouldSort);
}
/** * Build flow restriction rule mapping * @param list: Current limiting rules list * @param Function<FlowRule, K> groupFunction: One java 8 Medium Function, Used to get the rule name resourceName * private static final Function<FlowRule, String> extractResource = new Function<FlowRule, String>() { * @Override * public String apply(FlowRule rule) { * return rule.getResource(); * } * }; * @param filter: One java 8 Medium Predicate, Used to filter some rules * @param shouldSort: Is it sorted? * * @return Map<K, List<FlowRule>> among key Is the asset name ,value For all current assets FlowRule( adopt Set The value after de duplication ) */
public static <K> Map<K, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Function<FlowRule, K> groupFunction,
Predicate<FlowRule> filter, boolean shouldSort) {
Map<K, List<FlowRule>> newRuleMap = new ConcurrentHashMap<>();
if (list == null || list.isEmpty()) {
return newRuleMap;
}
// Create a temporary rule mapping
Map<K, Set<FlowRule>> tmpMap = new ConcurrentHashMap<>();
for (FlowRule rule : list) {
// Check if it is valid
if (!isValidRule(rule)) {
RecordLog.warn("[FlowRuleManager] Ignoring invalid flow rule when loading new flow rules: " + rule);
continue;
}
// Filter
if (filter != null && !filter.test(rule)) {
continue;
}
if (StringUtil.isBlank(rule.getLimitApp())) {
// set default LimitApp: The call source of the flow control ,default, Represents that the call source is not distinguished
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
// According to rules , Create the corresponding Flow shaping controller : Used to convert disordered traffic (request), Become orderly
TrafficShapingController rater = generateRater(rule);
rule.setRater(rater);
// Get the asset name ,resource
K key = groupFunction.apply(rule);
if (key == null) {
continue;
}
Set<FlowRule> flowRules = tmpMap.get(key);
if (flowRules == null) {
// Use hash set here to remove duplicate rules.
flowRules = new HashSet<>();
tmpMap.put(key, flowRules);
}
// Add rules to set aggregate
flowRules.add(rule);
}
Comparator<FlowRule> comparator = new FlowRuleComparator();
for (Entry<K, Set<FlowRule>> entries : tmpMap.entrySet()) {
List<FlowRule> rules = new ArrayList<>(entries.getValue());
if (shouldSort) {
// Sort the rules. Sort
Collections.sort(rules, comparator);
}
newRuleMap.put(entries.getKey(), rules);
}
return newRuleMap;
}
/** * According to rules , Create the corresponding Flow shaping controller : Used to convert disordered traffic (request), Become orderly */
private static TrafficShapingController generateRater(/*@Valid*/ FlowRule rule) {
// QPS
if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {
// According to the correspondence controlBehavior Generate TrafficShapingController
switch (rule.getControlBehavior()) {
case RuleConstant.CONTROL_BEHAVIOR_WARM_UP:
return new WarmUpController(rule.getCount(), rule.getWarmUpPeriodSec(),
ColdFactorProperty.coldFactor);
case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:
return new RateLimiterController(rule.getMaxQueueingTimeMs(), rule.getCount());
case RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:
return new WarmUpRateLimiterController(rule.getCount(), rule.getWarmUpPeriodSec(),
rule.getMaxQueueingTimeMs(), ColdFactorProperty.coldFactor);
case RuleConstant.CONTROL_BEHAVIOR_DEFAULT:
default:
// Default mode or unknown mode: default traffic shaping controller (fast-reject).
}
}
// Default TrafficShapingController
return new DefaultController(rule.getCount(), rule.getGrade());
}
}
4. Flow shaping controller (TrafficShapingController)
Flow control is a common concept in network transmission , It is used to adjust the sending data of network packets . However , From the perspective of system stability , In terms of the speed of processing requests , There is also a lot of stress . Requests coming at any time are often random and uncontrollable , And the processing power of the system is limited . We need to control the flow according to the processing capacity of the system .Sentinel As a tuner , You can adjust random requests to the right shape as needed , As shown in the figure below :
Flow control has the following angles :
- Resource call relationship , For example, the call link of resources , The relationship between resources ;
- Operation index , for example QPS、 Thread pool 、 System load, etc ;
- The effect of control , For example, direct current limiting 、 Cold start 、 Line up, etc .
Sentinel The design concept is to let you freely choose the angle of control , And flexible combination , So as to achieve the desired effect .
Sentinel Of TrafficShapingController There are two ways :
public interface TrafficShapingController {
/** * Check whether the current resource node can pass count Count . * * @param node Resource nodes * @param acquireCount Statistics count * @param prioritized Priority * @return true - adopt ; false - Refuse */
boolean canPass(Node node, int acquireCount, boolean prioritized);
/** * Check whether the current resource node can pass count Count . * * @param node Resource nodes * @param acquireCount Statistics count * @return true - adopt ; false - Refuse */
boolean canPass(Node node, int acquireCount);
}
TrafficShapingController There are four implementation classes , As shown in the figure below :
DefaultController: A default flow shaping controller , According to the flow limiting dimension of the defined resource ( Number of threads or QPS) To determine whether the current request is flow Limited ( Counter algorithm ).RateLimiterController: Uniform flow shaping controller , Make disorderly requests , Line up at a constant speed , Extra requests , Will refuse ( Leaky bucket algorithm ).WarmUpController: Preheating flow shaping controller , Slowly increase the flow , To protect system resources ( Implementation of token bucket algorithm , Through the system startup time , To increase the size of the token bucket , Until maximum )WarmUpRateLimiterController:RateLimiterController + WarmUpController
3.2 Define resources
What we call resources , It could be anything , service , Methods in service , Even a piece of code .
First, define the resources that may need to be protected , Then configure the rules . It can also be understood as , As long as there are resources , We can flexibly define various flow control rules at any time . When coding , Just consider whether the code needs to be protected , If you need protection , Define it as a resource . Such as :
// Resource name can use any string with business semantics , For example, method name 、 Interface name or other uniquely identifiable string .
try (Entry entry = SphU.entry("resourceName")) {
// Protected business logic
// do something here...
} catch (BlockException ex) {
// Resource access blocked , Be restricted or degraded
// Do the corresponding processing operation here
}
For mainstream frameworks , We offer adaptation , Just follow the instructions in the adapter ,Sentinel The services provided will be defined by default , Methods are resources .
3.3 Check if the rules are in effect
Define the rules and After resources , We check whether the current rules are effective , adopt Entry entry = SphU.entry Judge whether the current flow is limited , If SphU.entry The method works normally , The code system is normal , If an exception is thrown , It means that current limiting is required .
3.3.1 SphU
Basic for recording statistics and performing rule checks on resources Sentinel API.
conceptually , The physical or logical resources that need to be protected should be surrounded by a code block . If all current resource flow limiting rules are met , The request will be released . When the threshold of any current asset definition is exceeded , Will throw out BlockException.
SphU There are many methods defined in , as follows :
The main methods can be divided into two categories :
static Entry entry: Make statistics 、 Rule checking , If released , be Sync Call current “ resources ”.static AsyncEntry asyncEntry: Make statistics 、 Rule checking , If released , be asynchronous Call current “ resources ”.
1. static Entry entry
public class CtSph implements Sph {
private static final Object[] OBJECTS0 = new Object[0];
// The same resources share the same ProcessorSlotChain( Processing links ): cache
private static volatile Map<ResourceWrapper, ProcessorSlotChain> chainMap = new HashMap<ResourceWrapper, ProcessorSlotChain>();
// Object lock
private static final Object LOCK = new Object();
/** * The final method of execution * * @param resourceWrapper Asset wrapper object , Contains the resource name (resource name)、 Flow type (IN- Enter into 、OUT- Out )、 The resource type ( Ordinary 、web、rpc、api gateway、db sql) * @param count Count the number of requests * @param args For parameter flow control or custom slots args */
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
// Get the execution context object of the current thread Context: The bottom is ThreadLocal<Context> contextHolder
// It must be empty for the first time
Context context = ContextUtil.getContext();
if (context instanceof NullContext) {
// The {@link NullContext} indicates that the amount of context has exceeded the threshold,
// so here init the entry only. No rule checking will be done.
// NullContext: Indicates that the number of contexts has exceeded the threshold ( A tree structure , Maximum level 2000)
// So here we only initialize entries . There will be no rule checking .chain = null
return new CtEntry(resourceWrapper, null, context);
}
// Initialize a default context
if (context == null) {
// Using default context.
context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
}
// Global switch is close, no rule checking will do.
// Global switch , There will be no rule checking , Constant values Constants.ON = true
if (!Constants.ON) {
// There will be no rule checking .chain = null
return new CtEntry(resourceWrapper, null, context);
}
// Load slot chain
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
/* * Means amount of resources (slot chain) exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE}, * so no rule checking will be done. */
// Indicates that the slot chain exceeds Constants.MAX_SLOT_CHAIN_SIZE= 6000
if (chain == null) {
// There will be no rule checking .chain = null
return new CtEntry(resourceWrapper, null, context);
}
// Create a belt chain Of CtEntry
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
// adopt chain.entry Execute processing link , Complete asset collection 、 Statistics 、 flow control 、 Fusing the drop
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
// The traffic execution threshold at the current time is reached , perform exit, sign out
e.exit(count, args);
// Throw out BlockException
throw e1;
} catch (Throwable e1) {
// This should not happen, unless there are errors existing in Sentinel internal.
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
}
}
Pass above Entry Object creation process , And perform the final link analysis , It mainly involves several objects 、 Method :Context、ProcessorSlot<Object> chain、chain.entry(context, resourceWrapper, null, count, prioritized, args);、entry.exit(count, args), The following is about these objects and methods , Explain separately .
Context
Context Throughout all of the links in a call Entry.Context Maintain the metadata of the current call chain : Entry node 、 This time, call the link node 、 Call source and other information , adopt ThreadLocal Pass on .
above entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args) There is a piece of code that creates the context :
// Constants.CONTEXT_DEFAULT_NAME = "sentinel_default_context"
context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
Call the following :
public class CtSph implements Sph {
private final static class InternalContextUtil extends ContextUtil {
static Context internalEnter(String name) {
return trueEnter(name, "");
}
static Context internalEnter(String name, String origin) {
return trueEnter(name, origin);
}
}
}
InternalContextUtil by CtSph A static inner class of , Inherited from ContextUtil, Will eventually call ContextUtil Medium trueEnter(String name, String origin) Method .
public class ContextUtil {
// ThreadLocal
private static ThreadLocal<Context> contextHolder = new ThreadLocal<>();
// Context name node mapping
private static volatile Map<String, DefaultNode> contextNameNodeMap = new HashMap<>();
// Reentrant lock
private static final ReentrantLock LOCK = new ReentrantLock();
// Empty context object
private static final Context NULL_CONTEXT = new NullContext();
/** * Get the current thread Context object * @param name context name * @param origin source */
protected static Context trueEnter(String name, String origin) {
// Get the context object of the current thread
Context context = contextHolder.get();
if (context == null) {
Map<String, DefaultNode> localCacheNameMap = contextNameNodeMap;
// Get current name Of Node object
DefaultNode node = localCacheNameMap.get(name);
if (node == null) {
// Determine whether the maximum Node cache 2000
if (localCacheNameMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
setNullContext();
return NULL_CONTEXT;
} else {
// Lock : Reentrant lock
LOCK.lock();
try {
node = contextNameNodeMap.get(name);
// Double check
if (node == null) {
// Determine whether the maximum Node cache 2000
if (contextNameNodeMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
setNullContext();
return NULL_CONTEXT;
} else {
// Create a Node
node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);
// Add to all node collections
Constants.ROOT.addChild(node);
// Add cache
Map<String, DefaultNode> newMap = new HashMap<>(contextNameNodeMap.size() + 1);
newMap.putAll(contextNameNodeMap);
newMap.put(name, node);
contextNameNodeMap = newMap;
}
}
} finally {
LOCK.unlock();
}
}
}
// establish Context
context = new Context(node, name);
context.setOrigin(origin);
// add
contextHolder.set(context);
}
return context;
}
ProcessorSlot chain = lookProcessChain(resourceWrapper)
public class CtSph implements Sph {
// ProcessorSlotChain cache : Each resource corresponds to a ProcessorSlotChain
private static volatile Map<ResourceWrapper, ProcessorSlotChain> chainMap
= new HashMap<ResourceWrapper, ProcessorSlotChain>();
private static final Object LOCK = new Object();
// Get the current resource slot chain
ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
// From the cache
ProcessorSlotChain chain = chainMap.get(resourceWrapper);
if (chain == null) {
// Lock
synchronized (LOCK) {
// Double check
chain = chainMap.get(resourceWrapper);
if (chain == null) {
// The maximum number of resources :6000
if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
return null;
}
// Create a new slot chain
chain = SlotChainProvider.newSlotChain();
// cache
Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
chainMap.size() + 1);
newMap.putAll(chainMap);
newMap.put(resourceWrapper, chain);
chainMap = newMap;
}
}
}
return chain;
}
chain = SlotChainProvider.newSlotChain(); Create a new slot chain , The code is as follows :
public final class SlotChainProvider {
private static volatile SlotChainBuilder slotChainBuilder = null;
// Create a new slot : adopt SlotChainBuilder Object building
public static ProcessorSlotChain newSlotChain() {
if (slotChainBuilder != null) {
return slotChainBuilder.build();
}
// SPI: obtain SlotChainBuilder, And load the first instance or default value
slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault();
if (slotChainBuilder == null) {
// Should not go through here.
RecordLog.warn("[SlotChainProvider] Wrong state when resolving slot chain builder, using default");
slotChainBuilder = new DefaultSlotChainBuilder();
} else {
RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: {}",
slotChainBuilder.getClass().getCanonicalName());
}
// Build the slot chain
return slotChainBuilder.build();
}
private SlotChainProvider() {
}
}
Through the top Sentinel Of SPI Mechanism , obtain SlotChainBuilder, And load the first instance or default value , as follows ,SlotChainBuilder There is only one implementation com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder.
Build the slot chain slotChainBuilder.build(), The code is as follows :
@Spi(isDefault = true)
public class DefaultSlotChainBuilder implements SlotChainBuilder {
@Override
public ProcessorSlotChain build() {
// First create a default ProcessorSlotChain
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
// SPI Mechanism : Get all ProcessorSlot( default 、 Self defined ), And sort and return
List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
for (ProcessorSlot slot : sortedSlotList) {
// Remove dirty data
if (!(slot instanceof AbstractLinkedProcessorSlot)) {
RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain");
continue;
}
chain.addLast((AbstractLinkedProcessorSlot<?>) slot);
}
return chain;
}
}

each Slot Its function is as follows :
- NodeSelectorSlot: The path responsible for collecting resources , And the call path of these resources , Store in a tree structure , Used to limit the flow according to the call path 、 Downgrade .
- ClusterBuilderSlot: Statistics used to store assets , And caller information , for example , Of this resource RT( Mean response time )、QPS、Thread Count wait , This information will be used as a multi-dimensional limit 、 The basis of the downgrade .
- StatisticsSlot: Used to record 、 Statistics of different dimensions of runtime Indicator monitoring information .
- ParamFlowSlot: Used for resource configuration hotspot parameters 、 Current limiting rules and the previous slot The state of Statistics , To control the flow .
- SystemSlot: Through the state of the system , for example load1 etc. , To control the total inlet flow .
- AuthoritySlot: According to the configured black and white list and call source information , To do black and white list control .
- FlowSlot: It is used according to the preset current limiting rules and the front slot The state of Statistics , To control the flow .
- DegradeSlot: Through statistics and preset rules , To do the fusing degradation .
chain.entry
Build through the above slot link ( The chain of responsibility model ), Return to one ProcessorSlot<Object> chain object , And wrapped in CtEntry in . adopt chain.entry Complete the implementation of the responsibility chain .
About all Slot chain Implementation , Please refer to 【Alibaba Sentinel - Slot chain analysis 】.
entry.exit(count, args)
sign out : Clear context .
class CtEntry extends Entry {
@Override
public void exit(int count, Object... args) throws ErrorEntryFreeException {
trueExit(count, args);
}
@Override
protected Entry trueExit(int count, Object... args) throws ErrorEntryFreeException {
exitForContext(context, count, args);
return parent;
}
protected void exitForContext(Context context, int count, Object... args) throws ErrorEntryFreeException {
if (context != null) {
// NullContext immediate withdrawal
if (context instanceof NullContext) {
return;
}
// Judge the current processing Entry Is it this: If not , Circular recursive call exit, Deal with the following scenarios
// -parent
// ---asyncInvocation
// -----handleResultForAsync
// -------handleResultForAsync
if (context.getCurEntry() != this) {
String curEntryNameInContext = context.getCurEntry() == null ? null
: context.getCurEntry().getResourceWrapper().getName();
// Clean previous call stack.
CtEntry e = (CtEntry) context.getCurEntry();
// Circular recursive call
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 {
// Go through the onExit hook of all slots.
// To perform all slot chain Of exit Method
if (chain != null) {
chain.exit(context, resourceWrapper, count, args);
}
// Go through the existing terminate handlers (associated to this invocation).
// Call exit handler and clean
callExitHandlersAndCleanUp(context);
// Restore the call stack.
context.setCurEntry(parent);
if (parent != null) {
((CtEntry) parent).child = null;
}
if (parent == null) {
// Default context (auto entered) will be exited automatically.
// Default context ( Auto input ) Will automatically exit .
if (ContextUtil.isDefaultContext(context)) {
ContextUtil.exit();
}
}
// Clean the reference of context in current entry to avoid duplicate exit.
// Clean up references to the context in the current entry , Avoid repeated exits .
clearEntryContext();
}
}
}
}
2. static AsyncEntry asyncEntry
public class CtSph implements Sph {
private static final Object[] OBJECTS0 = new Object[0];
// The same resources share the same ProcessorSlotChain( Processing links ): cache
private static volatile Map<ResourceWrapper, ProcessorSlotChain> chainMap = new HashMap<ResourceWrapper, ProcessorSlotChain>();
// Object lock
private static final Object LOCK = new Object();
@Override
public AsyncEntry asyncEntry(String name, EntryType type, int count, Object... args) throws BlockException {
StringResourceWrapper resource = new StringResourceWrapper(name, type);
return asyncEntryInternal(resource, count, args);
}
private AsyncEntry asyncEntryInternal(ResourceWrapper resourceWrapper, int count, Object... args)
throws BlockException {
return asyncEntryWithPriorityInternal(resourceWrapper, count, false, args);
}
private AsyncEntry asyncEntryWithPriorityInternal(ResourceWrapper resourceWrapper, int count, boolean prioritized,
Object... args) throws BlockException {
// Get the execution context object of the current thread Context: The bottom is ThreadLocal<Context> contextHolder
// It must be empty for the first time
Context context = ContextUtil.getContext();
if (context instanceof NullContext) {
// The {@link NullContext} indicates that the amount of context has exceeded the threshold,
// so here init the entry only. No rule checking will be done.
// NullContext: Indicates that the number of contexts has exceeded the threshold ( A tree structure , Maximum level 2000)
// So here we only initialize entries . There will be no rule checking .chain = null
return asyncEntryWithNoChain(resourceWrapper, context);
}
// Initialize a default context
if (context == null) {
// Using default context.
context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
}
// Global switch is turned off, so no rule checking will be done.
// Global switch , There will be no rule checking , Constant values Constants.ON = true
if (!Constants.ON) {
// There will be no rule checking .chain = null
return asyncEntryWithNoChain(resourceWrapper, context);
}
// Load slot chain
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
// Means processor cache size exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE}, so no rule checking will be done.
// Indicates that the slot chain exceeds Constants.MAX_SLOT_CHAIN_SIZE= 6000
if (chain == null) {
// There will be no rule checking .chain = null
return asyncEntryWithNoChain(resourceWrapper, context);
}
// Create an asynchronous Entry object
AsyncEntry asyncEntry = new AsyncEntry(resourceWrapper, chain, context);
try {
// adopt chain.entry Execute processing link , Complete asset collection 、 Statistics 、 flow control 、 Fusing the drop
chain.entry(context, resourceWrapper, null, count, prioritized, args);
// The asynchronous context is started only when the entry successfully passes through the slot chain .
asyncEntry.initAsyncContext();
// The asynchronous call may take time in background, and current context should not be hanged on it.
// Delete the current asynchronous entry from the current context .
asyncEntry.cleanCurrentEntryInLocal();
} catch (BlockException e1) {
// When blocked, the async entry will be exited on current context.
// The async context will not be initialized.
// The traffic execution threshold at the current time is reached , perform exit, sign out
asyncEntry.exitForContext(context, count, args);
// Throw out BlockException
throw e1;
} catch (Throwable e1) {
// This should not happen, unless there are errors existing in Sentinel internal.
// When this happens, async context is not initialized.
RecordLog.warn("Sentinel unexpected exception in asyncEntryInternal", e1);
asyncEntry.cleanCurrentEntryInLocal();
}
return asyncEntry;
}
}
It can be seen that ,static AsyncEntry asyncEntry And static Entry entry The biggest difference , After the request passes all the rules of the current resource , Will initialize an asynchronous call chain .
SphU.asyncEntry(xxx) Will not affect the present ( Calling thread ) Of Context, So here are two entry On the call chain is a level relationship ( On the same floor ), Instead of nesting :
// The call chain is similar to :
// -parent
// ---asyncResource
// ---syncResource
asyncEntry = SphU.asyncEntry(asyncResource);
entry = SphU.entry(normalResource);
If you need to nest other resource calls in an asynchronous callback ( Whether it's entry still asyncEntry), Just use Sentinel Context switching function provided , To pass through in a corresponding place ContextUtil.runOnContext(context, f) Conduct Context Transformation , Transfer the corresponding resources to Context Switch to generated asynchrony Context, To maintain the correct call link relationship . Examples are as follows :
public void handleResult(String result) {
Entry entry = null;
try {
entry = SphU.entry("handleResultForAsync");
// Handle your result here.
} catch (BlockException ex) {
// Blocked for the result handler.
} finally {
if (entry != null) {
entry.exit();
}
}
}
public void someAsync() {
try {
AsyncEntry entry = SphU.asyncEntry(resourceName);
// Asynchronous invocation.
doAsync(userId, result -> {
// Context transformation in asynchronous callbacks , adopt AsyncEntry Of getAsyncContext Method to get asynchronous Context
ContextUtil.runOnContext(entry.getAsyncContext(), () -> {
try {
// Normal resource calls are nested here .
handleResult(result);
} finally {
entry.exit();
}
});
});
} catch (BlockException ex) {
// Request blocked.
// Handle the exception (e.g. retry or fallback).
}
}
The call chain is similar to :
-parent
---asyncInvocation
-----handleResultForAsync
边栏推荐
- 从零开始实现lmax-Disruptor队列(六)Disruptor 解决伪共享、消费者优雅停止实现原理解析
- C traps and defects Chapter 3 semantic "traps" 3.9 integer overflow
- mysql大表联合查询优化,大事务优化,规避事务超时,锁等待超时与锁表
- 【FreeSwitch开发实践】media bug获取通话语音流
- Verilog's time system tasks - $time, $stime, $realtime
- Let's talk about the summary of single merchant function modules
- MySQL - the difference between count (field), count (primary key), count (1), count (*)
- VASP calculation task error: M_ divide:can not subdivide 8 nodes by 6
- C陷阱与缺陷 第3章 语义“陷阱” 3.4 避免“举偶法”
- MYSQL入门与进阶(十三)
猜你喜欢

navicat新建数据库

Weekly recommended short videos: how to make product development more effective?

西瓜书学习第六章---SVM

Add a row to a specific location in the dataframe

Apache文件管理自学笔记——映射文件夹和基于单ip多域名配置apache虚拟机

Unity 之游戏特效

Interpreting AI robots' pet raising and leading fashion trends
![[freeswitch development practice] unimrcp compilation and installation](/img/ef/b82326152326293bf98e89da28b887.png)
[freeswitch development practice] unimrcp compilation and installation

百度副总裁李硕:数字技术加持下中国劳动力成本上升是好事

基于单片机烟雾温湿度甲醛监测设计
随机推荐
Add a row to a specific location in the dataframe
Analyzing the subjective consciousness of emotional resonance between robots and human beings
生产部署zabbix5.0笔记
Li Shuo, vice president of Baidu: it's a good thing that China's labor costs rise with the support of digital technology
万字详解 Google Play 上架应用标准包格式 AAB
Available data sets for scene classification tasks (part)
Pgzero aircraft war
IDEA安装后无法启动
"PHP Basics" output approximate value of PI
Analysis of concepts and terms in data warehouse
【FreeSwitch开发实践】UniMRCP编译与安装
Algorithm --- paint the house (kotlin)
Analysis of Project-based Learning Creativity in steam Education
SAP 中国本地化内容汇总
C traps and defects Chapter 3 semantic "traps" 3.9 integer overflow
Redis configuration cache expiration listening event trigger
C陷阱与缺陷 第2章 语法“陷阱” 2.6 “悬挂”else引发的问题
C语言程序设计 | 交换二进制数奇偶位(宏实现)
C陷阱与缺陷 第3章 语义“陷阱” 3.7 求值顺序
What is SOA (Service Oriented Architecture)?