当前位置:   article > 正文

分布式锁初探--redis锁_sessioncallback sessioncallback = new ses

sessioncallback sessioncallback = new sessioncallback()

前言

最近开发任务需要在多个时间点执行,时间点一部分由定时任务设定,一部分由mq监听来执行,但必须保证同一时间点只能有一个任务来执行。我们知道jdk自身提供的锁只能适用用单机应用,但如今分布式架构应用越来越多,多个应用部署集群时,我们可能需要某个时刻只有一个应用执行,这时就需要用到分布式锁。

分布锁基本应用场景及设计

###案例
有2个计算基金涨跌幅数据服务,一个是定时计算,一个是在每天基金净值更新后发送到mq,然后由多个监听服务去计算,净值更新时间是不固定的,因此计算时间也不固定。所以2个计算涨跌幅服务的服务会发生冲突,如何保证在同一时间只有一个任务执行,那就需要用到分布式锁。在这个场景中我用的是redis作为分布式锁来实现的。
这里写图片描述
上图基本为实现这个案例的流程,设计服务时我们需要保证以下几点:
1、分布锁必须保证多个服务请求同一个锁时绝对互斥。
2、需要设计超时间,防止任务停止或服务阻塞时锁无法释放,从而引发下一次任务一直无法获取锁。

Redis具体实现分布式锁

redis实现分布锁是通过SETNX 命令来实现的,具体格式

SETNX key value
  • 1

命令的作用是如果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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

升级到2.1又后,可以直接用

setIfAbsent(k key,V value,long timeout,TimeUnit unit);
  • 1

后续

以上为我所用到的reids作为分布式锁的一个应用场景,或许你已经看出上面应用案列中有可能的问题。因为本业务对互斥要求不高,所以即使重复计算也不会有多大问题,但对于互斥要求高的服务或许需要考虑的更多。
比如redis宕机了,该如何保证redis的可用性。即使是用redis的主从集群复制,主挂了,从可以接替上,但是依然会出现问题,因为redis主从复制是异步的,没办法保证主挂了,从节点上一定有锁数据。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/318500
推荐阅读
相关标签
  

闽ICP备14008679号