Spring Boot 中使用 Redis + Aop 进行限流

Spring Boot 中使用 Redis 进行限流,通常你可以采用如下几种方式:

  1. 令牌桶算法(Token Bucket)
  2. 漏桶算法(Leaky Bucket)
  3. 固定窗口计数器(Fixed Window Counter)
  4. 滑动日志窗口(Sliding Log Window)

实现 Redis 限流,可以采用 Redis 提供的数据结构和功能脚本,如 Lua 脚本、Redisson 库等。以下是使用 Redis 和 Lua 脚本来实现令牌桶限流算法的示例:

步骤一:编写 Lua 脚本。

下面是一个限流的 Lua 脚本示例,实现基本的限流功能,放在Spring Boot项目下的resources目录下。

--获取KEY
local key = KEYS[1] -- 限流的 key

local limit = tonumber(ARGV[1]) --注解标注的限流次数

local curentLimit = tonumber(redis.call('get', key) or "0")

if curentLimit + 1 > limit
then return 0
else
    -- 自增长 1
    redis.call('INCRBY', key, 1)
    -- 设置过期时间
    redis.call('EXPIRE', key, ARGV[2])
    return curentLimit + 1
end

步骤二:定义限流注解

package your.package;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface RedisLimit {
    /**
     * 资源的key,唯一
     * 作用:不同的接口,不同的流量控制
     */
    String key() default "";

    /**
     * 最多的访问限制次数
     */
    long permitsPerSecond() default 2;

    /**
     * 过期时间也可以理解为单位时间,单位秒,默认60
     */
    long expire() default 60;


    /**
     * 得不到令牌的提示语
     */
    String msg() default "系统繁忙,请稍后再试.";
}


步骤三:定义Aop切面类

package your.package;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Limit AOP
 */
@Slf4j
@Aspect
@Component
public class RedisLimitAop {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Pointcut("@annotation(your.package.RedisLimit)")
    private void check() {

    }

    private DefaultRedisScript<Long> redisScript;

    @PostConstruct
    public void init() {
        redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Long.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua")));
    }


    @Before("check()")
    public void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        //拿到RedisLimit注解,如果存在则说明需要限流
        RedisLimit redisLimit = method.getAnnotation(RedisLimit.class);

        if (redisLimit != null) {
            //获取redis的key
            String key = redisLimit.key();
            String className = method.getDeclaringClass().getName();
            String name = method.getName();

            String limitKey = key + className + method.getName();

            log.info(limitKey);

            if (StringUtils.isEmpty(key)) {
                throw new RedisLimitException("key cannot be null");
            }

            long limit = redisLimit.permitsPerSecond();

            long expire = redisLimit.expire();

            List<String> keys = new ArrayList<>();
            keys.add(key);

            Long count = stringRedisTemplate.execute(redisScript, keys, String.valueOf(limit), String.valueOf(expire));

            log.info("Access try count is {} for key={}", count, key);

            if (count != null && count == 0) {
                log.debug("获取key失败,key为{}", key);
                throw new RedisLimitException(redisLimit.msg());
            }
        }

    }

}

步骤四:自定义Redis限流异常

package your.package;


/**
 * Redis限流自定义异常
 * @date 2023/3/10 21:43
 */
public class RedisLimitException extends RuntimeException{
 public RedisLimitException(String msg) {
  super( msg );
 }
}

步骤五:自定义ResultInfo返回实体

package your.package;


import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ResultInfo<T> {

    private String message;
    private String code;
    private T data;


    public ResultInfo(String message, String code, T data) {
        this.message = message;
        this.code = code;
        this.data = data;
    }

    public static ResultInfo error(String message) {
        return new ResultInfo(message,"502",null);
    }



}

步骤六:定义Controller接口

package your.package;


import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/limit/redis")
public class LimitRedisController {

    /**
     * 基于Redis AOP限流
     */
    @GetMapping("/test")
    @RedisLimit(key = "redis-limit:test", permitsPerSecond = 2, expire = 1, msg = "当前排队人数较多,请稍后再试!")
    public String test() {
        log.info("限流成功。。。");
        return "ok";
    }

}

效果测试

在这里插入图片描述

实现了上面的步骤之后,Spring Boot应用就可以通过AOP与Redis来进行API限流了。

相关推荐

  1. 【PHP】使用Redis对API进行

    2024-03-11 07:38:02       32 阅读
  2. RateLimiter 算法使用

    2024-03-11 07:38:02       9 阅读
  3. Springboot Redis Lua 分布式

    2024-03-11 07:38:02       49 阅读

最近更新

  1. adb 常用的命令总结

    2024-03-11 07:38:02       0 阅读
  2. gcc: options: -specs

    2024-03-11 07:38:02       0 阅读
  3. Python题解Leetcode Hot 100之栈和堆

    2024-03-11 07:38:02       0 阅读
  4. docker容器如何与本地配置文件关联

    2024-03-11 07:38:02       0 阅读
  5. SQL 字段类型-上

    2024-03-11 07:38:02       1 阅读
  6. C++ 入门04:数组与字符串

    2024-03-11 07:38:02       1 阅读
  7. 简谈设计模式之原型模式

    2024-03-11 07:38:02       1 阅读
  8. GPT带我学-设计模式-13策略模式

    2024-03-11 07:38:02       1 阅读
  9. 写一个字符设备的驱动步骤

    2024-03-11 07:38:02       1 阅读

热门阅读

  1. Lua 函数前的冒号和点号,你知道他们的区别吗?

    2024-03-11 07:38:02       21 阅读
  2. [2023年]-hadoop面试真题(一)

    2024-03-11 07:38:02       29 阅读
  3. C/C++关键字详解-----`const`的使用

    2024-03-11 07:38:02       24 阅读
  4. Spring Boot(六十六):集成Alibaba Druid 连接池

    2024-03-11 07:38:02       25 阅读
  5. API 管理调研

    2024-03-11 07:38:02       21 阅读
  6. pytorch单机多卡训练 logger日志记录和wandb可视化

    2024-03-11 07:38:02       19 阅读
  7. Apache 的安装与目录结构

    2024-03-11 07:38:02       23 阅读
  8. 【Docker】apache 容器化部署

    2024-03-11 07:38:02       28 阅读
  9. Apache Hive(三)

    2024-03-11 07:38:02       23 阅读
  10. 蓝桥集训之日期差值

    2024-03-11 07:38:02       29 阅读
  11. 计算机网络的相关概念

    2024-03-11 07:38:02       20 阅读