赞
踩
最近开发任务需要在多个时间点执行,时间点一部分由定时任务设定,一部分由mq监听来执行,但必须保证同一时间点只能有一个任务来执行。我们知道jdk自身提供的锁只能适用用单机应用,但如今分布式架构应用越来越多,多个应用部署集群时,我们可能需要某个时刻只有一个应用执行,这时就需要用到分布式锁。
###案例
有2个计算基金涨跌幅数据服务,一个是定时计算,一个是在每天基金净值更新后发送到mq,然后由多个监听服务去计算,净值更新时间是不固定的,因此计算时间也不固定。所以2个计算涨跌幅服务的服务会发生冲突,如何保证在同一时间只有一个任务执行,那就需要用到分布式锁。在这个场景中我用的是redis作为分布式锁来实现的。
上图基本为实现这个案例的流程,设计服务时我们需要保证以下几点:
1、分布锁必须保证多个服务请求同一个锁时绝对互斥。
2、需要设计超时间,防止任务停止或服务阻塞时锁无法释放,从而引发下一次任务一直无法获取锁。
redis实现分布锁是通过SETNX
命令来实现的,具体格式
SETNX key value
命令的作用是如果key存在,则什么都不做,并且返回0,如果key不存在则将key的值设置成value,并且返回1,该命令是原子性的。我们可以利用该命令来实现分布式锁。
主要代码:
//这里设置锁过期时间,防止在获得锁的服务阻塞或者崩溃引起的锁无法释放 boolean getlock = lockManager.aquireLock("redisKey"); //判断获取锁成功 if(getlock){ try{ //do some thing }finally { //释放锁 lockManager.releaseLock("redisKey"); } } //lockManager @Service public class LockServiceImpl implements LockManager { @Resource(name = "redisServiceImpl") private RedisManager redisManager; //设置锁时间为10分钟,避免停服务 时计算涨跌幅,锁没释放,可根据其它具体业务需求修改过期时间 private static final Long LOCK_EXPIRE = 10*60L; @Override public boolean aquireLock(String key) { boolean isSet = redisManager.put(key, "1", LOCK_EXPIRE, true); return isSet; } @Override public void releaseLock(String key){ redisManager.delete(key); } }
redisManager.put方法
//redis2.1之前,spring-data-redis不支持直接setnx 调过期时间,可用以下方法实现
String lockKey = event.getModel() + ":" + event.getAction() + ":" + event.getId() + ":" + event.getMessage_id(); log.info("lockKey : {}" , lockKey); // 使用sessionCallBack处理 SessionCallback<Boolean> sessionCallback = new SessionCallback<Boolean>() { List<Object> exec = null; @Override @SuppressWarnings("unchecked") public Boolean execute(RedisOperations operations) throws DataAccessException { operations.multi(); stringRedisTemplate.opsForValue().setIfAbsent(lockKey,JSON.toJSONString(event)); stringRedisTemplate.expire(lockKey,Constants.REDIS_KEY_EXPIRE_SECOND_1_HOUR, TimeUnit.SECONDS); exec = operations.exec(); if(exec.size() > 0) { return (Boolean) exec.get(0); } return false; } }; return stringRedisTemplate.execute(sessionCallback);
升级到2.1又后,可以直接用
setIfAbsent(k key,V value,long timeout,TimeUnit unit);
以上为我所用到的reids作为分布式锁的一个应用场景,或许你已经看出上面应用案列中有可能的问题。因为本业务对互斥要求不高,所以即使重复计算也不会有多大问题,但对于互斥要求高的服务或许需要考虑的更多。
比如redis宕机了,该如何保证redis的可用性。即使是用redis的主从集群复制,主挂了,从可以接替上,但是依然会出现问题,因为redis主从复制是异步的,没办法保证主挂了,从节点上一定有锁数据。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。