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();
        }
    }
  • 相关阅读:
    2013.4.15 Particle Swarm Optimization with Skyline Operator for Fast Cloudbased Web Service Composition
    Adaptive service composition in flexible processes
    2013.4.13 DomainSpecific Service Selection for Composite Services
    2013.4.14 Modeling and Algorithms for QoSAware Service Composition in VirtualizationBased Cloud Computing
    2013.5.29 Towards Networkaware Service Composition in the Cloud
    Efficient algorithms for Web services selection with endtoend QoS constraints
    SQL Server中常用的SQL语句
    接口限流自定义注解
    linux服务器生产环境搭建
    MVEL自定义函数重复掉用报错:duplicate function
  • 原文地址:https://www.cnblogs.com/tonggc1668/p/11220915.html
Copyright © 2011-2022 走看看