接口防暴击-springboot

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.lianjia.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 vo = template.opsForValue();
        vo.set(key, value, insTime, TimeUnit.SECONDS);
    }


    public void setIfAbsent(String key, Long timeSec, String value) {
        ValueOperations vo = template.opsForValue();
        if (vo.setIfAbsent(key, value)) {
            vo.set(key, value, timeSec, TimeUnit.SECONDS);
        }
    }

    public String getByKey(String key) {
        ValueOperations vo = template.opsForValue();
        String result = vo.get(key);
        return result;
    }

    public Long inCr(String key) {
        ValueOperations 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 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)