How Spring Cloud Sentinel @SentinelResource Enforces Flow Control and Circuit Breaking
This article explains how Spring Cloud Sentinel’s @SentinelResource annotation works, detailing its AOP interception, the responsibility‑chain slot mechanism, flow‑control and circuit‑breaker processing, and shows the relevant configuration and code examples for integrating Sentinel into a Spring Boot backend.
Spring Cloud Sentinel Dependency and Initialization
After adding the spring-cloud-starter-alibaba-sentinel dependency, Spring Boot loads configuration from spring.factories, including classes such as SentinelWebAutoConfiguration, SentinelWebAutoConfiguration, SentinelWebAutoConfiguration, etc.
Version used in this article: sentinel 1.8.0.
@SentinelResource Working Principle
The simplest way to configure flow‑control rules is using the @SentinelResource annotation, which can define flow‑control and fallback rules directly. Example:
@SentinelResource(value = "ResOrderGet",
fallback = "fallback",
fallbackClass = SentinelResourceExceptionHandler.class,
blockHandler = "blockHandler",
blockHandlerClass = SentinelResourceExceptionHandler.class)
@GetMapping("/order/get/{id}")
public CommonResult<StockModel> getStockDetails(@PathVariable Integer id) {
StockModel stockModel = new StockModel();
stockModel.setCode("STOCK=>1000");
stockModel.setId(id);
return CommonResult.success(stockModel);
}Spring AOP intercepts the getStockDetails method. The bean definition can be found in SentinelAutoConfiguration:
@Bean
@ConditionalOnMissingBean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}The SentinelResourceAspect processes the annotation as follows:
@Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")
public void sentinelResourceAnnotationPointcut() {}
@Around("sentinelResourceAnnotationPointcut()")
public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
Method originMethod = resolveMethod(pjp);
SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
String resourceName = getResourceName(annotation.value(), originMethod);
EntryType entryType = annotation.entryType();
int resourceType = annotation.resourceType();
Entry entry = null;
try {
entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());
Object result = pjp.proceed();
return result;
} catch (BlockException ex) {
return handleBlockException(pjp, annotation, ex);
} catch (Throwable ex) {
Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
throw ex;
}
if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
traceException(ex);
return handleFallback(pjp, annotation, ex);
}
throw ex;
}
}Summary of execution: AOP intercepts the method, SphU.entry applies flow‑control rules, then the business method is invoked. If a flow‑control rule is triggered, a BlockException is handled; otherwise, a fallback method may be called.
Responsibility Chain for Flow Control
The core processing method is SphU.entry, which initializes a responsibility chain and executes slots.
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
// Initialize responsibility chain
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
// Execute entry
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
e.exit(count, args);
// Exception will be handled by SentinelResourceAspect
throw e1;
} catch (Throwable e1) {
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
}The chain is built by DefaultSlotChainBuilder:
public class DefaultSlotChainBuilder implements SlotChainBuilder {
@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
// Load all ProcessorSlot implementations via SPI, sorted by Order
List<ProcessorSlot> sortedSlotList = SpiLoader.loadPrototypeInstanceListSorted(ProcessorSlot.class);
for (ProcessorSlot slot : sortedSlotList) {
if (!(slot instanceof AbstractLinkedProcessorSlot)) {
RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain");
continue;
}
// Add to chain tail
chain.addLast((AbstractLinkedProcessorSlot<?>) slot);
}
return chain;
}
}The default slot order (simplified) is:
NodeSelectorSlot
ClusterBuilderSlot
LogSlot
StatisticSlot
AuthoritySlot
SystemSlot
ParamFlowSlot
FlowSlot
DegradeSlot
FlowSlot Flow Control
The FlowSlot entry method checks flow rules and then fires the entry:
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
// Check flow
checkFlow(resourceWrapper, context, node, count, prioritized);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}The default controller uses a fast‑fail strategy:
@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
int curCount = avgUsedTokens(node);
if (curCount + acquireCount > count) {
return false;
}
return true;
}
private int avgUsedTokens(Node node) {
if (node == null) {
return DEFAULT_AVG_USED_TOKENS;
}
return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps());
}If the rule is not passed, a FlowException is thrown, which is later handled by the aspect.
DegradeSlot Circuit Breaking
The DegradeSlot implements circuit‑breaker logic with three states: OPEN, HALF_OPEN, and CLOSED, based on response‑time metrics.
Sentinel Web Interceptor
When the @SentinelResource annotation is not used, Sentinel applies flow control via a web interceptor defined in SentinelWebAutoConfiguration:
@Bean
@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true)
public SentinelWebInterceptor sentinelWebInterceptor(SentinelWebMvcConfig sentinelWebMvcConfig) {
return new SentinelWebInterceptor(sentinelWebMvcConfig);
}The interceptor’s preHandle method also calls SphU.entry to enforce the responsibility chain:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
String resourceName = getResourceName(request);
if (StringUtil.isEmpty(resourceName)) {
return true;
}
if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {
return true;
}
String origin = parseOrigin(request);
String contextName = getContextName(request);
ContextUtil.enter(contextName, origin);
Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry);
return true;
} catch (BlockException e) {
try {
handleBlockException(request, response, e);
} finally {
ContextUtil.exit();
}
return false;
}
}Reference Documentation
https://github.com/alibaba/Sentinel/wiki
https://martinfowler.com/bliki/CircuitBreaker.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
