import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;
import java.util.Collections;
@Component
@Slf4j
public class JedisClient {
@Autowired
JedisPool jedisPool;
/**
* 加锁
*
* @param lock 锁的key
* @param requestId 对应客户端的标志
* @param timeout 锁的过期时间
* @return true-加锁成功,false-加锁失败
*/
public boolean lock(String lock, String requestId, Long timeout) {
try (Jedis jedis = jedisPool.getResource()) {
SetParams setParams = SetParams.setParams().nx().px(timeout);
String result = jedis.set(lock, requestId, setParams);
return "OK".equalsIgnoreCase(result);
}
}
/**
* 解锁
*
* @param lock 锁的key
* @param requestId 对应客户端的标志
* @return true-解锁成功,false-解锁失败
*/
public boolean unlock(String lock, String requestId) {
try (Jedis jedis = jedisPool.getResource()) {
// 这段Lua代码的功能是 首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)
// 在eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,保证原子性,并且直到eval命令执行完成,Redis才会执行其他命令。
String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then" +
" return redis.call('del',KEYS[1])" +
" else" +
" return 0" +
" end";
Object result = jedis.eval(luaScript, Collections.singletonList(lock), Collections.singletonList(requestId));
return result.equals(1L);
}
}
加锁
加锁时StringRedisTemplate有对应的方法,可直接为不存在的键设置超时时间,成功返回 true, 失败返回 false
stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
/**
* 加锁
*
* @param lock 锁的key
* @param requestId 对应客户端的标志
* @param timeout 锁的过期时间
* @return true-加锁成功,false-加锁失败
*/
public boolean lock(String lock, String requestId, Long timeout) {
return stringRedisTemplate.opsForValue().setIfAbsent(lock, requestId, timeout, TimeUnit.SECONDS);
}
解锁
和Jedis相同,一样是执行Lua脚本
/**
* 解锁
*
* @param lock 锁的key
* @param requestId 对应客户端的标志
* @return true-解锁成功,false-解锁失败
*/
public boolean unlock(String lock, String requestId) {
String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then" +
" return redis.call('del',KEYS[1])" +
" else" +
" return 0" +
" end";
RedisScript<Long> script = new DefaultRedisScript<>(luaScript, Long.class);
Long result = stringRedisTemplate.execute(script, Collections.singletonList(lock), requestId);
Long success = 1L;
return success.equals(result);
}
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.14.1</version>
</dependency>
spring:
# redis
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: localhost
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: 123456
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Value("${spring.redis.database:0}")
private int database;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Bean
public Redisson redisson() {
Config config = new Config();
// 单机模式
config.useSingleServer()
.setDatabase(database)
.setAddress(host + ":" + port)
.setPassword(password);
return (Redisson) Redisson.create(config);
}
}
@Autowired
注解把 Redisson 注入进来RLock redissonLock = redisson.getLock("lock_key");
redissonLock.lock();
redissonLock.unlock();