在生产环境中,接口的稳定性直接影响整个系统的可用性,当突发流量到来时,如果没有合理的流量控制机制,很容易导致服务资源耗尽、响应超时甚至雪崩。Sentinel作为一款轻量级的流量控制组件,支持限流、熔断、降级等多种流量治理场景,而通过自定义注解可以将流量控制逻辑与业务代码解耦,让开发者更便捷地为接口配置限流熔断规则,实现平滑的流量管控。

核心原理说明
Sentinel的流量控制核心是基于滑动窗口的流量统计,通过定义资源来标识需要管控的接口,再为资源配置对应的限流规则、熔断规则。自定义注解的作用是将资源定义、规则配置与业务方法绑定,在方法执行前后通过AOP切面触发Sentinel的流量校验逻辑,不需要在业务代码中硬编码限流相关逻辑。
自定义注解设计
首先我们需要定义一个自定义注解,用来标识需要被Sentinel管控的接口方法,注解中可以包含资源名称、限流阈值、熔断阈值等配置项,方便后续在切面中读取这些参数来初始化Sentinel规则。
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SentinelLimit {
// 资源名称,默认为空,空时使用方法全路径作为资源名
String resourceName() default "";
// 限流阈值,每秒允许通过的请求数
double count() default 10;
// 熔断阈值,异常比例达到该值时触发熔断
double errorRatio() default 0.5;
// 熔断时长,单位秒
int fuseTime() default 5;
}
Sentinel规则初始化与AOP切面实现
接下来需要实现AOP切面,在标注了@SentinelLimit注解的方法执行前,先检查对应的Sentinel限流、熔断规则是否存在,不存在则初始化规则,然后触发Sentinel的流量校验,校验通过才执行原方法,校验失败则抛出对应的限流或熔断异常。
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@Aspect
@Component
public class SentinelLimitAspect {
// 存储已初始化的资源名称,避免重复初始化规则
private final List<String> initResourceList = new ArrayList<>();
@Around("@annotation(com.example.demo.annotation.SentinelLimit)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SentinelLimit annotation = method.getAnnotation(SentinelLimit.class);
// 获取资源名称,默认使用方法全路径
String resourceName = annotation.resourceName();
if (resourceName.isEmpty()) {
resourceName = method.getDeclaringClass().getName() + "." + method.getName();
}
// 初始化规则
initRules(resourceName, annotation);
Entry entry = null;
try {
// 触发Sentinel流量校验
entry = SphU.entry(resourceName);
// 校验通过,执行原方法
return joinPoint.proceed();
} catch (BlockException e) {
// 限流或熔断触发,返回友好提示
return "当前接口访问过于频繁,请稍后再试";
} finally {
if (entry != null) {
entry.exit();
}
}
}
private void initRules(String resourceName, SentinelLimit annotation) {
if (initResourceList.contains(resourceName)) {
return;
}
// 初始化限流规则
List<FlowRule> flowRules = FlowRuleManager.getRules();
if (flowRules == null) {
flowRules = new ArrayList<>();
}
FlowRule flowRule = new FlowRule();
flowRule.setResource(resourceName);
flowRule.setCount(annotation.count());
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRules.add(flowRule);
FlowRuleManager.loadRules(flowRules);
// 初始化熔断规则
List<DegradeRule> degradeRules = DegradeRuleManager.getRules();
if (degradeRules == null) {
degradeRules = new ArrayList<>();
}
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource(resourceName);
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
degradeRule.setCount(annotation.errorRatio());
degradeRule.setTimeWindow(annotation.fuseTime());
degradeRules.add(degradeRule);
DegradeRuleManager.loadRules(degradeRules);
initResourceList.add(resourceName);
}
}
业务接口使用自定义注解
在需要限流熔断的业务接口方法上直接标注@SentinelLimit注解,配置对应的参数即可,不需要在方法内部编写任何流量控制相关代码。
import com.example.demo.annotation.SentinelLimit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
@SentinelLimit(resourceName = "testApi", count = 20, errorRatio = 0.6, fuseTime = 10)
public String testApi() {
// 模拟业务处理
return "接口调用成功";
}
}
生产环境注意事项
- 资源名称尽量保持唯一,建议使用业务模块加接口路径的命名方式,方便后续规则管理和监控。
- 限流阈值和熔断阈值不要写死在注解中,可以结合配置中心动态读取,方便线上实时调整规则,不需要重启服务。
- Sentinel的监控数据可以对接Prometheus等监控系统,实时观察接口的流量、限流、熔断情况,及时调整规则适配实际业务流量。
- 对于核心接口,建议同时配置限流和熔断规则,限流控制正常流量,熔断应对接口出现异常时的流量快速失败,避免影响上游服务。
常见问题排查
如果注解没有生效,首先检查AOP切面是否被Spring容器扫描到,其次确认Sentinel的依赖是否正确引入,最后检查资源名称是否重复或者规则初始化是否成功。如果限流阈值不生效,可以查看Sentinel的日志,确认规则是否被正确加载。