package cn.com.qmth.examcloud.web.helpers;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.StringUtils;
import org.assertj.core.util.Arrays;
import com.google.common.collect.Lists;
import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
import cn.com.qmth.examcloud.commons.util.ThreadLocalUtil;
import cn.com.qmth.examcloud.web.enums.HttpServletRequestAttribute;
import cn.com.qmth.examcloud.web.exception.SequenceLockException;
import cn.com.qmth.examcloud.web.interceptor.SeqlockInterceptor;
import cn.com.qmth.examcloud.web.redis.RedisClient;
import cn.com.qmth.examcloud.web.support.ServletUtil;
import cn.com.qmth.examcloud.web.support.SpringContextHolder;
/**
* 并发顺序锁
*
* @author WANGWEI
* @date 2019年2月22日
* @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
*/
public class SequenceLockHelper {
private static final ExamCloudLog LOG = ExamCloudLogFactory.getLog(SequenceLockHelper.class);
private static final String LOCK_PREFIX = "$_LOCK_C:";
private static RedisClient redisClient;
private static RedisClient getRedisClient() {
if (null == redisClient) {
redisClient = SpringContextHolder.getBean(RedisClient.class);
}
return redisClient;
}
/**
* 获取顺序请求锁
*
* @param args
* @throws SequenceLockException
* 获取锁失败时抛出异常
*/
public static void getLock(Object... args) throws SequenceLockException {
String key = LOCK_PREFIX + StringUtils.join(Arrays.asList(args), "_");
int timeout = 60 * 2;
if (getRedisClient().setIfAbsent(key, ThreadLocalUtil.getTraceId(), timeout)) {
if (LOG.isDebugEnabled()) {
LOG.debug("locked");
}
@SuppressWarnings("unchecked")
List keyList = (List) ServletUtil.getRequest()
.getAttribute(HttpServletRequestAttribute.$_CUSTOM_SEQUENCE_LOCK.name());
if (null == keyList) {
keyList = Lists.newArrayList();
}
keyList.add(key);
ServletUtil.getRequest().setAttribute(
HttpServletRequestAttribute.$_CUSTOM_SEQUENCE_LOCK.name(), keyList);
} else {
Long expire = getRedisClient().getExpire(key, TimeUnit.SECONDS);
if (null == expire || expire > timeout) {
getRedisClient().delete(key);
}
throw new SequenceLockException("请求等待,请稍后重试!");
}
}
/**
* 释放锁
* 接口调用不要调用此方法释放锁,此锁会在拦截器<{@link SeqlockInterceptor}中释放.
*
* @author WANGWEI
* @param args
*/
public static void releaseLock(Object... args) {
String key = LOCK_PREFIX + StringUtils.join(Arrays.asList(args), "_");
getRedisClient().expire((String) key, 100, TimeUnit.MILLISECONDS);
@SuppressWarnings("unchecked")
List keyList = (List) ServletUtil.getRequest()
.getAttribute(HttpServletRequestAttribute.$_CUSTOM_SEQUENCE_LOCK.name());
if (null != keyList) {
if (keyList.contains(key)) {
keyList.remove(key);
}
}
}
/**
* 获取顺序请求锁
*
* @param args
* @throws SequenceLockException
* 获取锁失败时抛出异常
*/
public static void getLockSimple(Object... args) throws SequenceLockException {
String key = LOCK_PREFIX + StringUtils.join(Arrays.asList(args), "_");
int timeout = 60 * 2;
if (!getRedisClient().setIfAbsent(key, ThreadLocalUtil.getTraceId(), timeout)) {
Long expire = getRedisClient().getExpire(key, TimeUnit.SECONDS);
if (null == expire || expire > timeout) {
getRedisClient().delete(key);
}
throw new SequenceLockException("请求等待,请稍后重试!");
}
}
/**
* 释放锁
* 接口调用不要调用此方法释放锁,此锁会在拦截器<{@link SeqlockInterceptor}中释放.
*
* @author WANGWEI
* @param args
*/
public static void releaseLockSimple(Object... args) {
String key = LOCK_PREFIX + StringUtils.join(Arrays.asList(args), "_");
getRedisClient().delete(key);
}
}