接口防暴擊-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 {
    /**
     * 過期時(shí)間,單位秒,默認(rèn)5分鐘, 5分鐘后可以再次提交.
     * 或者執(zhí)行完畢之后也可再次提交;
     * isWait為True時(shí)的最大等待時(shí)間
     */
    int expire() default 300;

    /**
     * 獲取不到全局鎖時(shí)是否要等待
     * @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("任務(wù)處理中, 請(qǐng)不要重復(fù)提交,key={}", key);
                throw new IllegalStateException("任務(wù)處理中, 請(qǐng)不要重復(fù)提交. ");
            }
            long waitTime = 0;
            while (waitTime < expire) {
                Thread.sleep(2000);
                waitTime += 2;
                logger.info("再次嘗試獲取全局鎖, dupKey: {}, 等待時(shí)間: {}/{} s", key, waitTime, expire);
                isSucc = redisService.setnx(key, Long.valueOf(String.valueOf(singleSubmit.expire())), uuid);
                if (isSucc) {
                    break;
                }
            }
            if (!isSucc) {
                logger.info("任務(wù)處理中,獲取全局鎖超時(shí). 等待時(shí)間,waitTime={},key={}", waitTime, key);
                throw new IllegalStateException("任務(wù)處理中,獲取全局鎖超時(shí). 等待時(shí)間: " + waitTime);
            }
        }

        try {
            Object obj = pjp.proceed();
            return obj;
        } catch (Exception e) {
            redisService.del(key);
            logger.error(e.getMessage(), e);
            throw new IllegalStateException("任務(wù)處理中,獲取全局鎖超時(shí). 等待時(shí)間: " + 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;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容