Sentinel控制http访问频率

250 阅读2分钟

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(), "系统繁忙,请稍后重试!");
        }
    }
}

五、具体使用

  1. 接口类
/**
 * @author: yangzc
 * @date: 2024/11/25 15:40
 * @Version: 1.0
 * @description:
 */
public interface ExternalFacade {

    void testMethod();
}
  1. 接口实现类
/**
 * @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请求
    }
}

六、请求调用

  1. 可以使用自动化测试工具,设置多线程去请求接口,如apifox;
  2. 自己创建一个线程池多线程调用,观察接口返回结果。