spring cloud 用redis做分布式锁(解决分布式多节点情况下,定时任务多次执行)
我们知道现在微服务很流行,为此,许多中小型企业都将自己以前的框架加以改造,其中以SpringCloud为最多,但是SpringCloud如果要加定时任务的话,在单台服务器上很好支持,
但是涉及到集群服务(多台服务的话)就要用到分布式锁了,最简单的方案是用Redis,好了废话不多说,直接上代码.
1、分布式锁
/** * 分布式锁 * * */ @Component public class DistributedLockHandler { private static final Logger logger = LoggerFactory.getLogger(DistributedLockHandler.class); /** * 最大持有锁的时间(毫秒) */ private final static long LOCK_EXPIRE = 30 * 1000L; /** * 尝试获取锁的时间间隔(毫秒) */ private final static long LOCK_TRY_INTERVAL = 30L; /** * 获取锁最大等待时间( 毫秒 ) */ private final static long LOCK_TRY_TIMEOUT = 20 * 1000L; @Resource(name = "customRedisTemplate") private RedisTemplate<String, String> template; /** * 尝试获取 分布式锁 * * @param lockKey * 锁名 * @return true 得到了锁 ,false 获取锁失败 */ public boolean tryLock(String lockKey) { return getLock(lockKey, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, LOCK_TRY_INTERVAL); } /** * 尝试获取 分布式锁(不自动释放锁) * * @param lockKey * 锁名 * @return true 得到了锁 ,false 获取锁失败 */ public boolean tryLockNotAutoRelease(String lockKey) { return getLock(lockKey, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, -1); } /** * 尝试获取 分布式锁 * * @param lockKey * 锁名 * @param timeout * 获取锁最大等待时间 * @return true 得到了锁 ,false 获取锁失败 */ public boolean tryLock(String lockKey, long timeout) { return getLock(lockKey, timeout, LOCK_TRY_INTERVAL, LOCK_EXPIRE); } /** * 尝试获取 分布式锁(不自动释放锁) * * @param lockKey * 锁名 * @param timeout * 获取锁最大等待时间 * @return true 得到了锁 ,false 获取锁失败 */ public boolean tryLockNotAutoRelease(String lockKey, long timeout) { return getLock(lockKey, timeout, LOCK_TRY_INTERVAL, -1); } /** * 尝试获取 分布式锁 * * @param lockKey * 锁名 * @param timeout * 获取锁最大等待时间 * @param tryInterval * 获取锁尝试 时间间隔 * @return true 得到了锁 ,false 获取锁失败 */ public boolean tryLock(String lockKey, long timeout, long tryInterval) { return getLock(lockKey, timeout, tryInterval, LOCK_EXPIRE); } /** * 尝试获取 分布式锁(不释放锁) * * @param lockKey * 锁名 * @param timeout * 获取锁最大等待时间 * @param tryInterval * 获取锁尝试 时间间隔 * @return true 得到了锁 ,false 获取锁失败 */ public boolean tryLockNotAutoRelease(String lockKey, long timeout, long tryInterval) { return getLock(lockKey, timeout, tryInterval, -1); } /** * 尝试获取 分布式锁 * * @param lockKey * 锁名 * @param timeout * 获取锁最大等待时间 * @param tryInterval * 获取锁尝试 时间间隔 * @param lockExpireTime * 锁最大持有时间 * @return true 得到了锁 ,false 获取锁失败 */ public boolean tryLock(String lockKey, long timeout, long tryInterval, long lockExpireTime) { return getLock(lockKey, timeout, tryInterval, lockExpireTime); } /** * 获取分布式锁 * * @param lockKey * 锁名 * @param timeout * 获取锁最大等待时间 * @param tryInterval * 获取锁尝试 时间间隔 * @param lockExpireTime * 锁最大持有时间 * @return true 得到了锁 ,false 获取锁失败 */ public boolean getLock(String lockKey, long timeout, long tryInterval, long lockExpireTime) { try { if (StringUtils.isEmpty(lockKey)) { return false; } long startTime = System.currentTimeMillis(); do { ValueOperations<String, String> ops = template.opsForValue(); if (ops.setIfAbsent(lockKey, "lockValue")) { if (lockExpireTime > 0) { template.expire(lockKey, lockExpireTime, TimeUnit.MILLISECONDS); } return true; } Thread.sleep(tryInterval); } while (System.currentTimeMillis() - startTime < timeout); } catch (InterruptedException e) { logger.error(e.getMessage()); return false; } return false; } /** * 释放锁 * * @param lockKey */ public void unLock(String lockKey) { if (!StringUtils.isEmpty(lockKey)) { template.delete(lockKey); } } }
2、 在定时任务中使用
@Component public class AutoComfirmReceied { private static final Logger logger = LoggerFactory.getLogger(AutoComfirmReceied.class); @Resource OrderService orderService; @Resource OrderItemMapper orderItemMapper; @Autowired DistributedLockHandler distributedLockHandler; @Scheduled(cron = "${cron.autoComfirmReceied}") public void run() { String lockKey = RedisKeyResolver.getLockkey( "AutoComfirmReceied:run" );; if( distributedLockHandler.tryLockNotAutoRelease( lockKey ) ) { try { runTask(); } catch (Exception e) { e.printStackTrace(); }finally { distributedLockHandler.unLock( lockKey ); } }else { if( logger.isDebugEnabled() ) { logger.debug("没有获取锁超时.............."); } } } private void runTask() { logger.info("3分钟执行一次定时任务" + System.currentTimeMillis()); List<String> orderItemReturnStatus = new ArrayList<>(); orderItemReturnStatus.add(OrderItemReturnStatus.WTH.name()); orderItemReturnStatus.add(OrderItemReturnStatus.YJJ.name()); orderItemReturnStatus.add(OrderItemReturnStatus.YQX.name()); OrderItemExample orderItemExample = new OrderItemExample(); OrderItemExample.Criteria oderc = orderItemExample.createCriteria(); oderc.andIsDelEqualTo(false) .andOrderStatusEqualTo(OrderItemStatus.SENDED.name()) .andReturnStatusIn(orderItemReturnStatus) .andShippingStatusEqualTo(OrderItemShippingStatus.YFH.name()); List<OrderItem> orderItemList = orderItemMapper.selectByExample(orderItemExample); Set<String> set = new HashSet<>(); for (OrderItem orderItem : orderItemList) { Long sendTime10 = AutoConfirmReceivedDateUtil.getAdd10Day(orderItem.getSendDate(), 10); Long currentTime = new Date().getTime(); if(currentTime > sendTime10){ //当前时间大于,发货后10天的时间,则确认收货 set.add(orderItem.getLogisticsId() + "," + orderItem.getUserId()); } } if(!set.isEmpty()){ for (String orderItem : set) { String[] item = orderItem.split(","); StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("超过10天自动确认收货,logisticsId:" + item[0] + ",userId:" + item[1]); orderService.confirmReceive(Long.valueOf(item[0]), Long.valueOf(item[1])); stringBuffer.append(",成功!"); logger.info(stringBuffer.toString()); } } } }