Sentinel + AOP 控制Http访问频率
使用场景
系统需要调用第三方http服务,部分接口有请求频率限制,每分钟不超过60次,多线程并发数不能超过3。
Sentinel 介绍
- Sentinel 是阿里巴巴开源的一款面向分布式服务架构的轻量级流量控制组件。它可以用于限制对 HTTP 服务的访问频率,并且提供了丰富的流量控制策略,如基于 QPS(每秒查询率)、线程数等多种维度的控制。
实现方案
一、依赖导入
导入阿里巴巴Sentienl依赖和Spring AOP依赖
<dependencies>
<!-- 用于Spring Boot与Sentinel集成(可选,根据具体使用场景添加更多Sentinel相关模块)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- Spring AOP依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
二、初始化请求频率限制配置
不同频率限制类型枚举
@Getter
@AllArgsConstructor
public enum ExternalAppEnum {
FIRST("testHttpRequestResource", "第一个限制类型")
;
/**
* 资源类型
*/
private final String type;
/**
* 描述
*/
private final String message;
}
初始化限制条件:QPS为1,并发线程数最多为3
import cn.xdf.tourism.order.provider.external.enums.ExternalAppEnum;
import org.springframework.context.annotation.Configuration;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import static com.alibaba.csp.sentinel.slots.block.RuleConstant.FLOW_GRADE_QPS;
import static com.alibaba.csp.sentinel.slots.block.RuleConstant.FLOW_GRADE_THREAD;
/**
* @author yangzc
* @date 2024/12/6 14:31
* @description
*/
@Configuration
public class SentinelConfig {
@PostConstruct
public static void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
// QPS限制
rules.add(ruleConfig(ExternalAppEnum.FIRST, 1.0, FLOW_GRADE_QPS));
// 并发线程数限制
rules.add(ruleConfig(ExternalAppEnum.FIRST, 3.0, FLOW_GRADE_THREAD));
FlowRuleManager.loadRules(rules);
}
private static FlowRule ruleConfig(ExternalAppEnum externalApp, double count, int gradeType) {
FlowRule rule = new FlowRule();
// 定义资源名称,不同频率限制定义不同资源名称testHttpRequestResource
rule.setResource(externalApp.getType());
// 设置QPS限制为1,即每秒最多允许1次请求,可根据实际情况调整
rule.setCount(count);
rule.setGrade(gradeType);
return rule;
}
}
三、定义切面接口类注解
import cn.xdf.tourism.order.provider.external.enums.ExternalAppEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author yangzc
* @date 2024/12/6 14:38
* @description
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HttpRateLimitAnnotation {
ExternalAppEnum type();
}
四、切面逻辑实现
import cn.xdf.tourism.common.enums.ApiMsgEnum;
import cn.xdf.tourism.order.provider.common.exception.BizException;
import cn.xdf.tourism.order.provider.external.annotation.HttpRateLimitAnnotation;
import cn.xdf.tourism.order.provider.external.enums.ExternalAppEnum;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author yangzc
* @date 2024/12/6 14:40
* @description
*/
@Slf4j
@Aspect
@Component
public class HttpRateLimitAspect {
// 切面接口类全路径
@Pointcut("@annotation(com.*.*.external.annotation.HttpRateLimitAnnotation)")
public void rateLimitPointcut() {}
@Around("rateLimitPointcut()")
public Object rateLimit(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
HttpRateLimitAnnotation annotation = method.getAnnotation(HttpRateLimitAnnotation.class);
ExternalAppEnum type = annotation.type();
try (Entry entry = SphU.entry(type.getType())) {
// 如果没有被限流,允许请求继续执行
return joinPoint.proceed();
} catch (BlockException e) {
// 如果被限流了,进行相应处理,返回提示信息给客户端等
throw new BizException(ApiMsgEnum.SYSTEM_ERROR.getCode(), "系统繁忙,请稍后重试!");
}
}
}
五、具体使用
- 接口类
/**
* @author: yangzc
* @date: 2024/11/25 15:40
* @Version: 1.0
* @description:
*/
public interface ExternalFacade {
void testMethod();
}
- 接口实现类
/**
* @author: yangzc
* @date: 2024/11/25 15:43
* @Version: 1.0
* @description:
*/
@Service
public class TestExternalFacade implements ExternalFacade {
@Override
@HttpRateLimitAnnotation(type = ExternalAppEnum.WDT)
public void testMethod() {
// 可以是业务逻辑,可以是http请求
}
}
六、请求调用
- 可以使用自动化测试工具,设置多线程去请求接口,如apifox;
- 自己创建一个线程池多线程调用,观察接口返回结果。