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