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;
}
}