首页 > 数据库 >使用 redis 实现分布式接口限流注解 RedisLimit

使用 redis 实现分布式接口限流注解 RedisLimit

时间:2023-09-18 17:16:38浏览次数:41  
标签:RedisLimit redis 限流 key org import

前言

  • 很多时候,由于种种不可描述的原因,我们需要针对单个接口实现接口限流,防止访问次数过于频繁。这里就用 redis+aop 实现一个限流接口注解

@RedisLimit 代码

点击查看RedisLimit注解代码

import java.lang.annotation.*;

/**
 * 功能:分布式接口限流注解
 * @author love ice
 * @create 2023-09-18 15:43
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLimit {
    /**
     * redis中唯一key,一般用方法名字做区分
     * 作用: 针对不同接口,做不同的限流控制
     */
    String key() default "";

    /**
     * 限流时间内允许访问次数 默认1
     */
    long permitsPerSecond() default 1;

    /**
     * 限流时间,单位秒 默认60秒
     */
    long expire() default 60;

    /**
     * 限流提示信息
     */
    String msg() default "接口限流,请稍后重试";
}

AOP代码

点击查看aop代码

import com.aliyuncs.utils.StringUtils;
import com.test.redis.Infrastructure.annotation.RedisLimit;
import lombok.extern.slf4j.Slf4j;
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.Collections;
import java.util.List;

/**
 * redis限流切面
 *
 * @author love ice
 * @create 2023-09-18 15:44
 */
@Slf4j
@Aspect
@Component
public class RedisLimitAop {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private DefaultRedisScript<Long> redisScript;

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

    @Pointcut("@annotation(com.test.redis.Infrastructure.annotation.RedisLimit)")
    private void check() {

    }

    @Before("check()")
    private 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 + name;
            log.info("限流的key:{}", limitKey);

            if (StringUtils.isEmpty(key)) {
                // 这里是自定义异常,为了方便写成了 RuntimeException
                throw new RuntimeException("code:101,msg:接口中 key 参数不能为空");
            }

            long limit = redisLimit.permitsPerSecond();
            long expire = redisLimit.expire();
            // 把 key 放入 List 中
            List<String> keys = new ArrayList<>(Collections.singletonList(key));
            Long count = stringRedisTemplate.execute(redisScript, keys, String.valueOf(limit), String.valueOf(expire));
            log.info("Access try count is {} for key ={}", count, keys);

            if (count != null && count == 0) {
                log.debug("令牌桶={}, 获取令牌失效,接口触发限流", key);
                throw new RuntimeException("code:10X, redisLimit.msg()");
            }
        }
    }
}

lua脚本代码

注意:脚本代码是放在 resources 文件下的,它的类型是 txt,名称后缀是lua。如果你不想改名称,就使用我写好的全名--> rateLimiter.lua

点击查看脚本代码
--获取KEY
local key = KEYS[1]

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

最后为了照顾纯小白,给大家看一下我的目录结构

标签:RedisLimit,redis,限流,key,org,import
From: https://www.cnblogs.com/LoveBB/p/17712428.html

相关文章

  • Redis学习之共享session(单点登录)
    介绍为什么需要共享session?防止多个后端服务器的数据存储不一致,导致用户访问时出现未登录的情况。如何实现共享session?使用独立的内存存储来存放session实现key如何设计?为了安全性,随机生成token,而不是拼接用户信息,防止恶意伪造或爆破。选择何种value数据结构存放用户信......
  • redis配置参数说明
    redis.conf配置文件说明##########ADVANCEDCONFIG###########指定是否激活重置哈希,默认为开启activerehashingyes#aofrewrite过程中,是否采取增量"文件同步"策略,默认为"yes",而且必须为yes.rewrite过程中,每32M数据进行一次文件同步,这样可以减少"aof大文件"写入对磁盘......
  • redis主从复制
    相关配置a.replica-read-onlyyes:从节点开启只读模式b.master-authxxxx:主节点访问密码c.replicaofip端口:从哪个主节点进行复制相关命令inforeplication查看主从信息主从复制分类主从刚连接的时候,会进行全量同步;全同步后,会进行增量同步。a.全量复制 i.mast......
  • CPU/内存/磁盘/网络/redis/MQ测试工具合集
    闲余时间为大家整理了CPU性能测试、内存带宽测试、内存延迟测试、磁盘IOPS测试、网络测试、数据库测试、Kafka/rabbitMQ性能测试工具合集,后续也会对工具进行简单使用说明。序号工具名称监控策略及内容1UnixBench-5.1.4CPU性能测试2stream内......
  • GaussDB(for Redis)游戏实践:玩家下线行为上报
    本文分享自华为云社区《GaussDB(forRedis)游戏实践:玩家下线行为上报》,作者:GaussDB数据库为保护未成年人的身心健康,2007年国家推出网络游戏防沉迷系统,对未成年人的游戏时间进行限制。游戏厂家需要及时感知用户的下线时间并上报。Redis是游戏数据库重要选型之一,在基于开源Redis......
  • 深入探讨Spring Boot中的Redis缓存
    介绍Redis是一种高性能的内存数据库,常用于缓存和消息队列等场景。在SpringBoot中,我们可以通过集成Redis来实现缓存功能。本文将深入探讨SpringBoot中的Redis缓存,包括如何配置、如何使用以及一些注意事项。配置在SpringBoot中,我们可以通过在application.properties或applicati......
  • 【GO使用redis】GO语言使用Redis基础
    之前我为大家分享了php版本的对于redis的使用,当然我也是一个go的初学者,把自己在用go的时候对接redis的时候也记录一下,为大家分享一下。下面正式开始。redis有许多go语言的客户端包,都能实现对redis的操作。例如redigo、go-redis。我们可以随意选择想要安装的我们使用的是redigo,下......
  • Redis主从架构环境搭建(一主二从 + 3个sentinel)
    安装RedisServersudoadd-apt-repositoryppa:redislabs/redissudoaptupdatesudoaptinstallredis-serverredis-cli-h127.0.0.1-p6379pingsudosystemctlrestartredis-serverss-an|grep6379redis-server-vRedisserverv=7.0.12sha=00000000:0malloc=jem......
  • 【红包雨功能的】环境部署(弹性伸缩、负载均衡、Redis读写分离、云服务器部署)
    创建环境创建专用网络VPC安全组创建云服务器打包部署2.Java环境#下载jdk17wgethttps://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz#安装上传工具以后使用命令rz选中文件进行上传yuminstall-ylrzsz#解压tar-xzvfjdk-17_linux-x64_b......
  • SpringBoot Redis使用AOP防止重复提交
    自定义注解importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/***@ProjectName:gswr-ets-cloud*@ClassName:*@Description:防止重复提交的自定义......