SpringBoot项目中利用Redis实现分布式锁
实现步骤
1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
<version>2.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
2. 依赖说明
spring-boot-starter-data-redis:
在低版本中默认依赖 Jedis
在高版本中默认依赖 lettuce,若要使用Jedis 则需要修改依赖(如上所示)
3.SpringBoot配置Redis
application.properties 或者 application.yml 都可以(只是有格式的区别,作用并无差别)
spring.redis.host=127.0.0.1
spring.redis.database=0
spring.redis.port=6379
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
spring.redis.password=redis123456
4. 代码实现
package com.uzhizhe.user.cache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import java.util.Collections;
/**
* @Desc Redis service
* @Author uzhizhe
*/
@Service
@Slf4j
public class RedisService {
/**
* 操作成功
*/
private static final Long RELEASE_SUCCESS = 1L;
/**
* 操作成功
*/
private static final String LOCK_SUCCESS = "OK";
/**
* 不存在才set
*/
private static final String SET_IF_NOT_EXIST = "NX";
/**
* 毫秒单位
*/
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* 释放分布式锁的Lua 脚本
*/
private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
/**
* 默认分布式锁过期时间
*/
private static final Integer DEFALUT_EXPIRE_TIME = 5000;
/**
* redisTemplate
*/
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 获取分布式锁
*
* @param lockKey 加锁键
* @param requestId 加锁客户端唯一标识(采用UUID)
* @param expireTime 锁过期时间, 单位:毫秒
* @return 获取锁成功或者失败
*/
public boolean tryLock(String lockKey, String requestId, long expireTime) {
return redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
return LOCK_SUCCESS.equals(result);
});
}
/**
* 释放分布式锁
*
* @param lockKey lockKey
* @param requestId requestId 加锁时的唯一ID
* @return 释放锁成功或者失败
*/
public boolean releaseLock(String lockKey, String requestId) {
return redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
Object result = jedis.eval(RELEASE_LOCK_SCRIPT, Collections.singletonList(lockKey),
Collections.singletonList(requestId));
return RELEASE_SUCCESS.equals(result);
});
}
}
5. 最后几点说明
利用redis 的set方法实现加锁
1. set 的5个参数,能够很好的保证加锁的正确性
2. 过期时间:保证不会出现死锁,在没有主动释放时,会因过期释放
3. 设置Value值,保证自己的锁不会被别人释放(所谓:解铃还须系铃人)
4. Redis 命令的原子性,保证了锁的互斥性