reddison 实现滑动时间窗口限流

相关代码

1. 依赖

 <dependency>
   	  <groupId>org.redisson</groupId>
     <artifactId>redisson-spring-boot-starter</artifactId>
     <version>3.17.0</version>
 </dependency>

2. redission

   @Bean
    public RedissonClient redissonClient(){
        return Redisson.create();
    }

3. 注解

package com.jm.annotation;

import org.redisson.api.RateIntervalUnit;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SlidingWindowRateLimit {
    String key() default "";  // Redis key
    int rate() default 10;    // 允许的请求数
    int time() default 1;     // 时间窗口(秒)
    RateIntervalUnit timeUnit() default RateIntervalUnit.SECONDS;
    
    // 超时设置
    long timeout() default 0;  // 获取许可的超时时间,0表示不等待
    TimeUnit timeoutUnit() default TimeUnit.MILLISECONDS;  // 超时时间单位
    
    // 失败处理配置
    String fallbackMethod() default "";  // 失败时调用的回退方法名
    boolean throwException() default true;  // 是否抛出异常
    String errorMessage() default "Rate limit exceeded";  // 自定义错误消息
    Class<? extends RuntimeException> exceptionClass() default RuntimeException.class;  // 自定义异常类型
    
    // 日志配置
    boolean enableLog() default false;  // 是否启用日志记录
}

4. 切面

package com.jm.aop;

import com.jm.annotation.SlidingWindowRateLimit;
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.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class SlidingWindowRateLimitAspect {

    private static final Logger logger = LoggerFactory.getLogger(SlidingWindowRateLimitAspect.class);

    @Autowired
    private RedissonClient redissonClient;

    @Around("@annotation(slidingWindowRateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint, SlidingWindowRateLimit slidingWindowRateLimit) throws Throwable {
        String key = slidingWindowRateLimit.key();
        int rate = slidingWindowRateLimit.rate();
        int time = slidingWindowRateLimit.time();
        RateIntervalUnit timeUnit = slidingWindowRateLimit.timeUnit();
        long timeout = slidingWindowRateLimit.timeout();
        TimeUnit timeoutUnit = slidingWindowRateLimit.timeoutUnit();
        
        // 如果key为空,使用方法签名作为key
        if (key.isEmpty()) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            key = signature.getDeclaringType().getName() + "." + signature.getName();
        }

        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        rateLimiter.trySetRate(RateType.OVERALL, rate, time, timeUnit);

        boolean acquired;
        if (timeout > 0) {
            // 使用超时获取许可
            acquired = rateLimiter.tryAcquire(timeout, timeoutUnit);
            if (slidingWindowRateLimit.enableLog()) {
                logger.info("Rate limiter key: {}, timeout: {}{}, acquired: {}", 
                    key, timeout, timeoutUnit, acquired);
            }
        } else {
            // 不等待,立即获取许可
            acquired = rateLimiter.tryAcquire();
            if (slidingWindowRateLimit.enableLog()) {
                logger.info("Rate limiter key: {}, no timeout, acquired: {}", key, acquired);
            }
        }

        if (acquired) {
            return joinPoint.proceed();
        } else {
            return handleRateLimitFailure(joinPoint, slidingWindowRateLimit);
        }
    }

    /**
     * 处理限流失败的情况
     */
    private Object handleRateLimitFailure(ProceedingJoinPoint joinPoint, SlidingWindowRateLimit slidingWindowRateLimit) throws Throwable {
        String fallbackMethod = slidingWindowRateLimit.fallbackMethod();
        
        // 尝试调用回退方法
        if (!fallbackMethod.isEmpty()) {
            try {
                return invokeFallbackMethod(joinPoint, fallbackMethod);
            } catch (Exception e) {
                if (slidingWindowRateLimit.enableLog()) {
                    logger.error("Failed to invoke fallback method: {}", fallbackMethod, e);
                }
                // 如果回退方法调用失败,继续处理异常逻辑
            }
        }
        
        // 根据配置决定是否抛出异常
        if (slidingWindowRateLimit.throwException()) {
            String errorMessage = slidingWindowRateLimit.errorMessage();
            Class<? extends RuntimeException> exceptionClass = slidingWindowRateLimit.exceptionClass();
            
            if (slidingWindowRateLimit.enableLog()) {
                logger.warn("Rate limit exceeded for key: {}, message: {}", 
                    generateKey(joinPoint), errorMessage);
            }
            
            // 创建自定义异常实例
            RuntimeException exception;
            try {
                exception = exceptionClass.getConstructor(String.class).newInstance(errorMessage);
            } catch (Exception e) {
                // 如果无法创建自定义异常,使用默认的RuntimeException
                exception = new RuntimeException(errorMessage);
            }
            
            throw exception;
        }
        
        // 如果不抛出异常,返回null或默认值
        if (slidingWindowRateLimit.enableLog()) {
            logger.warn("Rate limit exceeded for key: {}, returning null", generateKey(joinPoint));
        }
        return null;
    }

    /**
     * 调用回退方法
     */
    private Object invokeFallbackMethod(ProceedingJoinPoint joinPoint, String fallbackMethodName) throws Throwable {
        Object target = joinPoint.getTarget();
        Class<?> targetClass = target.getClass();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?>[] parameterTypes = signature.getParameterTypes();
        
        try {
            // 尝试找到具有相同参数的回退方法
            Method fallbackMethod = targetClass.getDeclaredMethod(fallbackMethodName, parameterTypes);
            fallbackMethod.setAccessible(true);
            return fallbackMethod.invoke(target, joinPoint.getArgs());
        } catch (NoSuchMethodException e) {
            // 尝试找到无参数的回退方法
            try {
                Method fallbackMethod = targetClass.getDeclaredMethod(fallbackMethodName);
                fallbackMethod.setAccessible(true);
                return fallbackMethod.invoke(target);
            } catch (NoSuchMethodException ex) {
                throw new RuntimeException("Fallback method not found: " + fallbackMethodName, ex);
            }
        }
    }

    /**
     * 生成限流key
     */
    private String generateKey(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getDeclaringType().getName() + "." + signature.getName();
    }
}

5. 测试 方法

package com.jm.service.impl;

import com.jm.annotation.SlidingWindowRateLimit;
import org.redisson.api.RateIntervalUnit;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class SlidingWindowServiceImpl {

    /**
     * 基本限流示例 - 每秒最多5个请求
     */
    @SlidingWindowRateLimit(
        key = "basic-service",
        rate = 1,
        time = 1,
        timeUnit = RateIntervalUnit.SECONDS
    )
    public String basicRateLimit() {
        return "Basic rate limit success";
    }

    /**
     * 带超时的限流示例 - 等待最多500毫秒获取许可
     */
    @SlidingWindowRateLimit(
        key = "timeout-service",
        rate = 1,
        time = 1,
        timeUnit = RateIntervalUnit.SECONDS,
        timeout = 5000,
        timeoutUnit = TimeUnit.MILLISECONDS,
        enableLog = true
    )
    public String timeoutRateLimit() {
        return "Timeout rate limit success";
    }

    /**
     * 带回退方法的限流示例 - 限流时调用回退方法
     */
    @SlidingWindowRateLimit(
        key = "fallback-service",
        rate = 1,
        time = 1,
        timeUnit = RateIntervalUnit.SECONDS,
        fallbackMethod = "fallbackMethod",
        enableLog = true
    )
    public String fallbackRateLimit(String userId) {
        return "Fallback rate limit success for user: " + userId;
    }

    /**
     * 回退方法 - 参数匹配
     */
    private String fallbackMethod(String userId) {
        return "Fallback response for user: " + userId;
    }

    /**
     * 自定义异常的限流示例
     */
    @SlidingWindowRateLimit(
        key = "custom-exception-service",
        rate = 1,
        time = 2,
        timeUnit = RateIntervalUnit.SECONDS,
        errorMessage = "服务繁忙,请稍后重试",
        exceptionClass = ServiceBusyException.class,
        enableLog = true
    )
    public String customExceptionRateLimit() {
        return "Custom exception rate limit success";
    }

    /**
     * 不抛出异常的限流示例 - 返回null
     */
    @SlidingWindowRateLimit(
        key = "no-exception-service",
        rate = 1,
        time = 1,
        timeUnit = RateIntervalUnit.SECONDS,
        throwException = false,
        enableLog = true
    )
    public String noExceptionRateLimit() {
        return "No exception rate limit success";
    }

    /**
     * 复杂示例 - 结合多种配置
     */
    @SlidingWindowRateLimit(
        key = "complex-service",
        rate = 10,
        time = 1,
        timeUnit = RateIntervalUnit.MINUTES,
        timeout = 1000,
        timeoutUnit = TimeUnit.MILLISECONDS,
        fallbackMethod = "complexFallback",
        errorMessage = "请求过于频繁,请稍后再试",
        exceptionClass = TooManyRequestsException.class,
        enableLog = true
    )
    public String complexRateLimit(String operation, int count) {
        return String.format("Complex operation %s completed with count %d", operation, count);
    }

    /**
     * 复杂示例的回退方法
     */
    private String complexFallback(String operation, int count) {
        return String.format("Fallback: Operation %s is temporarily unavailable, count: %d", operation, count);
    }

    /**
     * 无参数回退方法示例
     */
    @SlidingWindowRateLimit(
        key = "no-param-fallback-service",
        rate = 1,
        time = 1,
        timeUnit = RateIntervalUnit.SECONDS,
        fallbackMethod = "noParamFallback",
        enableLog = true
    )
    public String noParamFallbackRateLimit() {
        return "No param fallback rate limit success";
    }

    /**
     * 无参数回退方法
     */
    private String noParamFallback() {
        return "Default fallback response";
    }

    /**
     * 自定义服务忙异常
     */
    public static class ServiceBusyException extends RuntimeException {
        public ServiceBusyException(String message) {
            super(message);
        }
    }

    /**
     * 自定义请求过多异常
     */
    public static class TooManyRequestsException extends RuntimeException {
        public TooManyRequestsException(String message) {
            super(message);
        }
    }
} 

6. 测试controller

package com.jm.controller;

import com.jm.service.impl.SlidingWindowServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author:JianWu
 * @Date: 2025/7/9  16:34
 */
@RestController
@RequestMapping("/slidingWindowLimiter")
public class SlidingWindowLimiterController {

    @Autowired
    private SlidingWindowServiceImpl slidingWindowService;

    @GetMapping("/basicRateLimit")
    public String basicRateLimit(){
        return slidingWindowService.basicRateLimit();
    }

    @GetMapping("/timeoutRateLimit")
    public String timeoutRateLimit(){
        return slidingWindowService.timeoutRateLimit();
    }

    @GetMapping("/fallbackRateLimit")
    public String fallbackRateLimit(){
        return slidingWindowService.fallbackRateLimit("ads");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值