美文网首页
接口防暴击-springboot

接口防暴击-springboot

作者: 酷酷的美猴王 | 来源:发表于2018-09-12 19:45 被阅读0次

    1:SingleSubmit

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface SingleSubmit {
        /**
         * 过期时间,单位秒,默认5分钟, 5分钟后可以再次提交.
         * 或者执行完毕之后也可再次提交;
         * isWait为True时的最大等待时间
         */
        int expire() default 300;
    
        /**
         * 获取不到全局锁时是否要等待
         * @return
         */
        boolean needWait() default false;
    
    }
    

    2:DupKey

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DupKey {
    
    }
    

    3:SingleSubmitAspect

    @Aspect
    @Component
    public class SingleSubmitAspect implements Ordered {
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Resource
        private RedisService redisService;
    
    
        private final static String PREFIX = "crm:";
    
        @Pointcut("@annotation(com.xiaohei.home.crm.web.aop.SingleSubmit)")
        public void pointCut() {
        }
    
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            Method method = ((MethodSignature) pjp.getSignature()).getMethod();
            SingleSubmit singleSubmit = (SingleSubmit) method.getAnnotation(SingleSubmit.class);
            if (singleSubmit == null) {
                return pjp.proceed();
            }
            boolean needWait = singleSubmit.needWait();
            int expire = singleSubmit.expire();
            if (expire <= 0) {
                return pjp.proceed();
            }
            String key = createKey(pjp, singleSubmit);
            if (Strings.isNullOrEmpty(key)) {
                Object obj = pjp.proceed();
                return obj;
            }
            String uuid = UUID.randomUUID().toString();
            boolean isSucc = redisService.setnx(key, Long.valueOf(String.valueOf(singleSubmit.expire())), uuid);
            if (!isSucc) {
                if (!needWait) {
                    logger.info("任务处理中, 请不要重复提交,key={}", key);
                    throw new IllegalStateException("任务处理中, 请不要重复提交. ");
                }
                long waitTime = 0;
                while (waitTime < expire) {
                    Thread.sleep(2000);
                    waitTime += 2;
                    logger.info("再次尝试获取全局锁, dupKey: {}, 等待时间: {}/{} s", key, waitTime, expire);
                    isSucc = redisService.setnx(key, Long.valueOf(String.valueOf(singleSubmit.expire())), uuid);
                    if (isSucc) {
                        break;
                    }
                }
                if (!isSucc) {
                    logger.info("任务处理中,获取全局锁超时. 等待时间,waitTime={},key={}", waitTime, key);
                    throw new IllegalStateException("任务处理中,获取全局锁超时. 等待时间: " + waitTime);
                }
            }
    
            try {
                Object obj = pjp.proceed();
                return obj;
            } catch (Exception e) {
                redisService.del(key);
                logger.error(e.getMessage(), e);
                throw new IllegalStateException("任务处理中,获取全局锁超时. 等待时间: " + e);
            }
        }
    
        private String createKey(ProceedingJoinPoint pjp, SingleSubmit singleSubmit) {
            MethodSignature method = (MethodSignature) pjp.getSignature();
            String className = pjp.getTarget().getClass().getName();
            String methodName = method.getMethod().getName();
            String prefix = StringUtils.join(PREFIX, ":", className, ":", methodName, ":");
            return getCacheKey(prefix, method.getMethod(), pjp.getArgs());
        }
    
        @Override
        public int getOrder() {
            return 1;
        }
    
        private String getCacheKey(String cacheKey, Method method, Object[] args) {
            Annotation[][] anns = method.getParameterAnnotations();
            StringBuffer sb = new StringBuffer(cacheKey);
            int count = 0;
            if (args != null && args.length > 0) {
                for (int i = 0; i < args.length; i++) {
                    if (!hasDupParam(anns[i])) {
                        count = count + 1;
                        continue;
                    } else if (args[i] == null) {
                        sb.append("/");
                    } else if (args[i] instanceof Date) {
                        sb.append("/").append(CommonDateFormat.dateFormatStirng((Date) (args[i])));
                    } else {
                        sb.append("/").append(args[i]);
                    }
                }
                if (count == args.length) {
                    logger.info("防暴击没有加DupKey");
                    return "";
                }
            }
            return sb.toString();
        }
    
        private boolean hasDupParam(Annotation[] anns) {
            for (Annotation ann : anns) {
                if (ann instanceof DupKey) {
                    return true;
                }
            }
            return false;
        }
    }
    

    4:redis-config

    public class RedisCacheConfiguration extends CachingConfigurerSupport {
        Logger logger = LoggerFactory.getLogger(RedisCacheConfiguration.class);
    
        @Value("${spring.redis.host}")
        private String host;
    
        @Value("${spring.redis.port}")
        private int port;
    
        @Value("${spring.redis.timeout}")
        private int timeout;
    
        @Value("${spring.redis.pool.max-idle}")
        private int maxIdle;
    
        @Value("${spring.redis.pool.max-wait}")
        private long maxWaitMillis;
    
        @Value("${spring.redis.password}")
        private String password;
    
        @Bean
        public JedisPool redisPoolFactory() {
            logger.info("JedisPool注入成功!!");
            logger.info("redis地址:" + host + ":" + port);
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxIdle(maxIdle);
            jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
    
            JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
    
            return jedisPool;
        }
    
    }
    

    5:redisService

    @Service
    public class RedisService {
    
        @Autowired
        private StringRedisTemplate template;
    
        public void set(String key, Long insTime, String value) {
            ValueOperations<String, String> vo = template.opsForValue();
            vo.set(key, value, insTime, TimeUnit.SECONDS);
        }
    
    
        public void setIfAbsent(String key, Long timeSec, String value) {
            ValueOperations<String, String> vo = template.opsForValue();
            if (vo.setIfAbsent(key, value)) {
                vo.set(key, value, timeSec, TimeUnit.SECONDS);
            }
        }
    
        public String getByKey(String key) {
            ValueOperations<String, String> vo = template.opsForValue();
            String result = vo.get(key);
            return result;
        }
    
        public Long inCr(String key) {
            ValueOperations<String, String> vo = template.opsForValue();
            Long v = vo.increment(key, 1L);
            return v;
        }
    
        /**
         * 判断key是否存在
         *
         * @param key
         * @return
         */
        public boolean exists(String key) {
            return template.hasKey(key);
        }
    
        public boolean setnx(String key, Long timeSec, String value) {
            boolean res = false;
            ValueOperations<String, String> vo = template.opsForValue();
            if (vo.setIfAbsent(key, value)) {
                vo.set(key, value, timeSec, TimeUnit.SECONDS);
                res = true;
                if (res && timeSec > 0) {
                    template.expire(key, timeSec, TimeUnit.SECONDS);
                }
            }
            return res;
        }
    }
    

    相关文章

      网友评论

          本文标题:接口防暴击-springboot

          本文链接:https://www.haomeiwen.com/subject/dnybgftx.html