分布式锁初探--redis锁

前言

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

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

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

Redis具体实现分布式锁

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主从复制是异步的,没办法保证主挂了,从节点上一定有锁数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值