Redis+Lua脚本+AOP+反射+自定义注解实现限流注解

controller接口

    /**
     * Redis+Lua脚本+AOP+反射+自定义注解实现限流
     *
     * @return
     */
    @GetMapping("/redis/limit/test")
    @RedisLimitAnnotation(key = "redisLimit",permitsPerSecond = 3,expire = 10,msg = "当前访问人多,请稍后再试!!!")
    public String redisLimitTest() {
    
    

        return "正常业务返回" + IdUtil.fastUUID();
    }

Lua脚本(rateLimiter.lua)

--获取key,针对那个接口进行限流,LUA脚本中的数组索引默认是从1开始的而不是从零开始。
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

自定义注解


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RedisLimitAnnotation {
    
    

    /**
     * 资源的key,唯一、
     * 作用: 不同的接口,不同的流量控制
     *
     * @return
     */
    String key() default "";

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

    /**
     * 过期时间(计算窗口时间),单位秒默认30
     *
     * @return
     */
    long expire() default 30;

    /**
     * 默认温馨提示语
     *
     * @return
     */
    String msg() default "default message:系统繁忙or你点击太快,请稍后再试,谢谢";


}

AOP


@Slf4j
@Aspect
@Component
public class RedisLimitAop {
    
    

    Object result = null;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

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


    @Around("@annotation(com.deloitte.services.duty.config.RedisLimitAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint) {
    
    
        System.out.println("========环绕通知=======");

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        Method method = signature.getMethod();

        RedisLimitAnnotation redisLimitAnnotation = method.getAnnotation(RedisLimitAnnotation.class);

        if (redisLimitAnnotation != null) {
    
    

            //获取redis的key
            String key = redisLimitAnnotation.key();

            String className = method.getDeclaringClass().getName();

            String methodName = method.getName();

            String limitKey = key + "\t" + className + "\t" + methodName;

            log.info(limitKey);

            if (null == key) {
    
    
                throw new RuntimeException("it's danger,limitKey cannot be null");
            }

            long limit = redisLimitAnnotation.permitsPerSecond();
            long expire = redisLimitAnnotation.expire();
            List<String> keys = new ArrayList<>();
            keys.add(key);

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

            if (null != count && count == 0) {
    
    
                System.out.println("启动限流功能key:" + key);
                return redisLimitAnnotation.msg();
            }


        }

        try {
    
    
            result = joinPoint.proceed();
        } catch (Throwable e) {
    
    
            throw new RuntimeException(e);
        }


        return result;

    }


}

猜你喜欢

转载自blog.csdn.net/m0_54712443/article/details/140303737