自学内容网 自学内容网

高效接口限流:基于自定义注解与RateLimiter的实践

在高并发场景下,接口的流量控制是保证系统稳定性和提升性能的关键之一。通过实现接口限流,我们可以有效避免系统在访问高峰时发生崩溃。本文将详细介绍如何通过自定义注解和切面编程结合RateLimiter来实现接口的限流功能,以应对高并发请求。

什么是RateLimiter?

RateLimiter是Guava提供的一个工具类,用于控制某些资源的访问频率。它通过令牌桶算法来限制并发请求的数量。我们可以通过设置每秒请求数(QPS)来控制接口的访问速率,避免瞬间请求过多导致系统过载。

实现思路

在本例中,我们通过以下几个步骤来实现接口限流:

  1. 自定义注解:我们定义一个@Limiter注解来标记需要进行限流的接口方法。
  2. 切面编程:利用Spring AOP技术,在执行标记了@Limiter注解的方法前后做处理。
  3. RateLimiter:根据QPSconcurrency参数,创建RateLimiter实例并在方法执行时控制访问速率。

一、定义Limiter注解

首先,我们定义一个@Limiter注解,用于标记哪些方法需要进行限流控制。注解中包含两个参数:QPS(每秒请求数)和concurrency(并发量)。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Limiter {
    double QPS() default Double.MAX_VALUE;  // 限流的QPS
    int concurrency() default 1;            // 允许的并发数
}

@Retention@Target注解指定了这个注解的作用范围和生命周期。QPS默认值为Double.MAX_VALUE,意味着不限制请求速率;concurrency默认值为1,表示每次只能允许1个请求并发执行。 

二、RateLimiter切面类实现

接下来,我们创建一个RateLimiterAspect切面类,利用Spring AOP拦截带有@Limiter注解的方法。我们根据注解的参数动态创建RateLimiter实例,并控制方法的执行。

@Aspect
@Component
public class RateLimiterAspect {
    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
    private Map<String, RateLimiter> limiterMap = new ConcurrentHashMap<>();

    public RateLimiterAspect() {}

    @Around("execution(* *(..)) && @annotation(limiter)")
    public Object around(ProceedingJoinPoint joinPoint, Limiter limiter) throws Throwable {
        // 获取当前方法名并根据QPS创建RateLimiter
        RateLimiter rateLimiter = getOrCreateRateLimiter(getMethodName(joinPoint), limiter.QPS());
        rateLimiter.acquire();  // 获取一个许可

        // 记录执行时间
        Stopwatch stopwatch = Stopwatch.createStarted();
        Object ret = joinPoint.proceed(joinPoint.getArgs());
        double elapsed = stopwatch.elapsed(TimeUnit.MICROSECONDS);
        double rate = TimeUnit.SECONDS.toMicros(1L) / elapsed * limiter.concurrency();
        
        // 设置当前方法的请求速率
        rateLimiter.setRate(rate);
        return ret;
    }

    private String getMethodName(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        return String.join(".", methodSignature.getDeclaringType().getSimpleName(), methodSignature.getName());
    }

    private RateLimiter getOrCreateRateLimiter(String method, double permitsPerSecond) {
        return limiterMap.computeIfAbsent(method, key -> RateLimiter.create(permitsPerSecond));
    }
}
切面解读
  1. @Around:这个注解表示我们将在方法执行之前和之后进行操作。通过execution(* *(..))来匹配所有方法,并且使用@annotation(limiter)来确保只拦截带有@Limiter注解的方法。
  2. RateLimiter:每次方法执行时,我们根据QPS值动态创建一个RateLimiter实例,使用rateLimiter.acquire()方法来获取许可,保证每秒的请求不会超过设定的QPS。
  3. 执行时间监控:通过Stopwatch来计算方法执行的时间,根据执行时间动态调整速率。

三、使用示例

假设我们有一个接口方法,需要限制每秒最多10个请求,并且允许3个并发请求:

@RestController
@RequestMapping("/u/user")
@CrossOrigin
public class UserController {

    @GetMapping("/Test")
    @Limiter(QPS = 10, concurrency = 3)
    public R Test(@RequestParam String username){
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(username + " " + "count:" + count++);
        return R.ok().message("test");
    }
}

在上面的例子中,getUserInfo方法被@Limiter注解标记,意味着每秒最多允许10个请求并发执行最多3次。

我们进行测压,检验结果

注解成功生效!!! 

测压的方案可以👀我另一篇文章​​​​​​:保姆级jmeter压测计算QPS教程_jmeter测试qps-CSDN博客


原文地址:https://blog.csdn.net/qq_62775328/article/details/144617289

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!