随着现在 分布式 架构越来越盛行,在很多场景下需要使用到 分布式锁 。分布式 锁 的实现有很多种,比如基于 数据库 、 zookeeper
等,本文主要介绍使用 Redis
做分布式锁的方式,并封装成 spring boot s tar ter,方便使用
一. Redis
分布式锁的实现以及存在的问题
锁是针对某个资源,保证其访问的互斥性,在实际使用当中,这个资源一般是一个字符串。使用 Redis
实现锁,主要是将资源放到 Redis
当中,利用其原子性,当其他 线程 访问时,如果 Redis
中已经存在这个资源,就不允许之后的一些操作。spring boot使用 Redis
的操作主要是通过 RedisTemplate
来实现,一般步骤如下:
- 将锁资源放入 Redis
(注意是当 key 不存在时才能放成功,所以使用 setIfAbsent
方法):
redisTemplate.opsForValue().setIfAbsent("key", "value");
- 设置过期 时间
redisTemplate.expire("key", 30000, TimeUnit.MILLISECONDS);
- 释放锁
redisTemplate.delete("key");
一般情况下,这样的实现就能够满足锁的 需求 了,但是如果在调用 setIfAbsent
方法之后线程挂掉了,即没有给锁定的资源设置过期时间,默认是永不过期,那么这个锁就会一直存在。所以需要保证设置锁及其过期时间两个操作的原子性,spring data的 RedisTemplate
当中并没有这样的方法。但是在jedis当中是有这种原子操作的方法的,需要通过 RedisTemplate
的 execute
方法获取到jedis里操作命令的对象, 代码 如下:
String result = redisTemplate.execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
return commands.set(key, "锁定的资源", "NX", "PX", expire);
}
});
注意:
Redis
从2.6.12版本开始 set
命令支持 NX
、 PX
这些 参数 来达到 setnx
、 setex
、 psetex
命令的效果,文档参见:
http ://doc.redisfans.com/string/set.html