springboot 支持分布式的接口限流, 通过自定义注解 支持限流时间、限流策略、限流的维度

4 篇文章 0 订阅
1 篇文章 0 订阅
  1. 首先,我们需要创建一个自定义注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    int value(); // 限流阈值
    long duration(); // 限流时间窗口
    RateLimitType type(); // 限流策略类型
    String limitKey() default "global"; // 限流维度,默认为全局维度
}
// 定义限流策略类型枚举
public enum RateLimitType {
	// 固定时间窗口
    FIXED_TIME_WINDOW,
    // 滑动时间窗口
    SLIDING_TIME_WINDOW,
    // 其他限流策略类型
}
  1. 然后,我们需要创建一个切面来处理这个注解:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class RateLimitAspect {

    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    @Around("@annotation(rateLimit)")
    public Object limit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        // 获取注解中的限流设置
        int limit = rateLimit.value();
        long duration = rateLimit.duration();
        RateLimitType type = rateLimit.type();
        String limitKey = rateLimit.limitKey ();

        // 生成key
        String key = generateKey(limitKey);

        // 根据限流策略类型,执行相应的限流策略
        boolean allowed = false;
        if (type == RateLimitType.FIXED_TIME_WINDOW) {
            allowed = fixedTimeWindowRateLimit(key, limit, duration);
        } else if (type == RateLimitType.SLIDING_TIME_WINDOW) {
            allowed = slidingTimeWindowRateLimit(key, limit, duration);
        } else {
            // 其他限流策略的实现
            // ...
        }

        if (!allowed) {
            throw new RuntimeException("Rate limit exceeded");
        }

        // 如果没有超过限流阈值,执行方法
        return joinPoint.proceed();
    }

    private String generateKey(String limitKey) {
        // 根据限流维度生成不同的key
        return "rate_limit:" + limitKey;
    }

    private boolean fixedTimeWindowRateLimit(String key, int limit, long duration) {
       String luaScript = "local current\n" +
            "current = redis.call('incr',KEYS[1])\n" +
            "if tonumber(current) == 1 then\n" +
            "redis.call('expire',KEYS[1],ARGV[2])\n" +
            "end\n" +
            "if tonumber(current) > tonumber(ARGV[1]) then\n" +
            "return 0\n" +
            "end\n" +
            "return 1";
    Long result = (Long) redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class), Collections.singletonList(key), limit, duration);
    return result != 0;
    }



    private boolean slidingTimeWindowRateLimit(String key, int limit, long duration) {
        String luaScript = "local current\n" +
            "current = redis.call('incr',KEYS[1])\n" +
            "if tonumber(current) == 1 then\n" +
            "redis.call('expire',KEYS[1],ARGV[2])\n" +
            "else if tonumber(current) > tonumber(ARGV[1]) then\n" +
            "redis.call('decr',KEYS[1])\n" +
            "return 0\n" +
            "end\n" +
            "end\n" +
            "return 1";
    Long result = (Long) redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class), Collections.singletonList(key), limit, duration);
    return result != 0;
    }
}

3.使用

你可以在你的方法上使用@RateLimit注解,并指定limitKey属性,例如

@RateLimit(value=10, duration=60, type=RateLimitType.FIXED_TIME_WINDOW, limitKey="#ip")@RateLimit(value=10, duration=60, type=RateLimitType.FIXED_TIME_WINDOW, limitKey="#userId")

这样,你就可以根据limitKey进行限流了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值