zoukankan      html  css  js  c++  java
  • Limit

    package me.zhengjie.common.aop.limit;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @author jacky
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Limit {
    
        // 资源名称,用于描述接口功能
        String name() default "";
    
        // 资源 key
        String key() default "";
    
        // key prefix
        String prefix() default "";
    
        // 时间的,单位秒
        int period();
    
        // 限制访问次数
        int count();
    
        // 限制类型
        LimitType limitType() default LimitType.CUSTOMER;
    
    }
    package me.zhengjie.common.aop.limit;
    
    public enum LimitType {
        CUSTOMER,
    //     by ip addr
        IP;
    }
    package me.zhengjie.common.aop.limit;
    
    import com.google.common.collect.ImmutableList;
    import me.zhengjie.common.exception.BadRequestException;
    import me.zhengjie.common.utils.IpUtil;
    import me.zhengjie.common.utils.RequestHolder;
    import org.apache.commons.lang3.StringUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.script.DefaultRedisScript;
    import org.springframework.data.redis.core.script.RedisScript;
    import org.springframework.stereotype.Component;
    
    
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Method;
    
    @Aspect
    @Component
    public class LimitAspect {
        @Autowired
        private RedisTemplate redisTemplate;
        private static final Logger logger = LoggerFactory.getLogger(LimitAspect.class);
    
    
        @Pointcut("@annotation(Limit)")
        public void pointcut() {
    //
        }
    
        @Around("pointcut()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            HttpServletRequest request = RequestHolder.getHttpServletRequest();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method signatureMethod = signature.getMethod();
            Limit limit = signatureMethod.getAnnotation(Limit.class);
            LimitType limitType = limit.limitType();
            String name = limit.name();
            String key = limit.key();
            if (StringUtils.isEmpty(key)) {
                switch (limitType) {
                    case IP:
                        key = IpUtil.getIP(request);
                        break;
                    default:
                        key = signatureMethod.getName();
                }
            }
    
            ImmutableList keys = ImmutableList.of(StringUtils.join(limit.prefix(), "_", key, "_", request.getRequestURI().replaceAll("/","_")));
    
            String luaScript = buildLuaScript();
            RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
            Number count = (Number) redisTemplate.execute(redisScript, keys, limit.count(), limit.period());
            if (null != count && count.intValue() <= limit.count()) {
                logger.info("第{}次访问key为 {},描述为 [{}] 的接口", count, keys, limit.name());
                return joinPoint.proceed();
            } else {
                throw new BadRequestException("访问次数受限制");
            }
        }
    
        /**
         * 限流脚本
         */
        private String buildLuaScript() {
            return "local c" +
                    "
    c = redis.call('get',KEYS[1])" +
                    "
    if c and tonumber(c) > tonumber(ARGV[1]) then" +
                    "
    return c;" +
                    "
    end" +
                    "
    c = redis.call('incr',KEYS[1])" +
                    "
    if tonumber(c) == 1 then" +
                    "
    redis.call('expire',KEYS[1],ARGV[2])" +
                    "
    end" +
                    "
    return c;";
        }
    }
    package me.zhengjie.monitor.rest;
    
    import me.zhengjie.common.aop.limit.Limit;
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    @RestController
    @RequestMapping("test")
    public class TestController {
        private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger();
    
        /**
         * 测试限流注解,下面配置说明该接口 60秒内最多只能访问 10次,保存到redis的键名为 limit_test,
         */
        @Limit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit")
        @GetMapping("limit")
        public int testLimit() {
            return ATOMIC_INTEGER.incrementAndGet();
        }
    }
  • 相关阅读:
    sql知识点记录
    makefile编译错误情况整理
    web worker 简介
    实现跨域访问的方法总结
    fiddler使用指南
    [转]SASS用法指南
    koa文档参考
    [转]html5: postMessage解决跨域和跨页面通信的问题
    [转]JavaScript ES6 class指南
    [转]前端利器:SASS基础与Compass入门
  • 原文地址:https://www.cnblogs.com/tonggc1668/p/11220915.html
Copyright © 2011-2022 走看看