此文档主要演示了如何使用 Spring Boot 集成RedisTemplate实现分布式锁。
pom.xml
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 对象池,使用redis时必须引入 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
RedisConfig
/**
* redis配置
* @author xiehengxing
* @date 2020/7/29 18:30
*/
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableCaching
public class RedisConfig {
/**
* 默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,因此支持序列化
*/
@Bean
public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
/**
* 配置使用注解的时候缓存配置,默认是序列化反序列化的形式,加上此配置则为 json 形式
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
// 配置序列化
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();
}
}
RedisLock
/**
* 分布式共享锁
* @author xiehengxing
* @date 2020/8/2 10:40
*/
@Slf4j
@Component
public class RedisLock {
@Autowired
private RedisTemplate redisTemplate;
/**
* 共享锁默认时长(秒)
*/
private static final long TIMEOUT = 60;
/**
* 超时时长(秒)
*/
private static final long OVERTIME = 6;
/**
* 获取锁
* @param key
* @param requestId
* @return
*/
public boolean lock(String key, String requestId){
long startTime = System.currentTimeMillis();
for(;;) {
boolean locked = redisTemplate.opsForValue().setIfAbsent(key, requestId, TIMEOUT, TimeUnit.SECONDS);
if (locked) {
return true;
}
if ((System.currentTimeMillis() - startTime)/1000 > OVERTIME) {
return false;
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
log.error("线程被中断" + Thread.currentThread().getId(), e);
}
}
}
/**
* 使用lua脚本解锁
* @param key
* @param requestId
* @return
*/
public boolean unlock(String key, String requestId) {
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(requestId)){
return false;
}
DefaultRedisScript<Long> redisScript = new DefaultRedisScript();
//用于解锁的lua脚本位置
redisScript.setScriptText(
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0" +
"end");
redisScript.setResultType(Long.class);
//没有指定序列化方式,默认使用上面配置的
Object result = redisTemplate.execute(redisScript, Collections.singletonList(key), requestId);
return result.equals(Long.valueOf(1));
}
}