分布式锁
分布式锁,即分布式系统中的锁。随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。
下面主要介绍springboot集成redis和Redission实现分布式锁。
1.引入依赖
<!-- redisson依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.40.2</version>
</dependency>
<!-- redis依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.4.1</version>
</dependency>
2.配置信息
spring:
application:
name: demo-server
# dynamic数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver # mysql版本为5.x使用该配置: com.mysql.jdbc.Driver,8.0版本使用:com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ftdb0?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: 123
# Redis配置
data:
redis:
# 主从模式配置
cluster:
nodes:
- 127.0.0.1:7003
- 127.0.0.1:7001
- 127.0.0.1:7002
- 127.0.0.1:7102
- 127.0.0.1:7202
- 127.0.0.1:7302
- 127.0.0.1:7101
- 127.0.0.1:7201
- 127.0.0.1:7301
- 127.0.0.1:7401
- 127.0.0.1:7402
# 单机配置
# host: 127.0.0.1
# port: 6379
# password: 11
redisson:
# 单机配置
# address: 127.0.0.1:6379
# password: 12345
# 主从模式配置
clusterServersConfig:
# 只在从服务节点里读取
readMode: "SLAVE"
# 订阅模式,主节点读取
subscriptionMode: "MASTER"
# 主节点信息
nodeAddresses:
- "redis://127.0.0.1:7001"
- "redis://127.0.0.1:7101"
- "redis://127.0.0.1:7102"
- "redis://127.0.0.1:7002"
- "redis://127.0.0.1:7201"
- "redis://127.0.0.1:7202"
- "redis://127.0.0.1:7003"
- "redis://127.0.0.1:7302"
- "redis://127.0.0.1:7301"
- "redis://127.0.0.1:7401"
- "redis://127.0.0.1:7402"
#集群扫描间隔时间 单位毫秒
scanInterval: 1000
3.Redis配置类
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.DefaultBaseTypeLimitingValidator;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class RedisConfig {
private final RedisConnectionFactory redisConnectionFactory;
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
ObjectMapper mapper = new ObjectMapper().
activateDefaultTyping(new DefaultBaseTypeLimitingValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY).
setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(mapper, Object.class);
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
Redisson实现分布式锁
package com.demo.server.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.common.exception.CommonException;
import com.demo.common.vo.UserTest;
import com.demo.server.mapper.UserTestMapper;
import com.demo.server.service.UserTestService;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@Service
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class UserServiceImpl extends ServiceImpl<UserTestMapper, UserTest> implements UserTestService {
private final RedissonClient client;
@Override
public int lockTest(long id) {
RLock lock = client.getLock("lock:user:" + id);
try {
// 尝试获取锁,最多等待1秒,上锁以后5秒自动解锁
boolean locked = lock.tryLock(1, 5, TimeUnit.SECONDS);
if (locked) {
return baseMapper.lockTest(id);
}
throw new RuntimeException("获取锁失败");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
finally { // 这里可以在测试时注释掉,不然会无法看到效果,也可以不用,因为在锁里面已经释放了
// 如果是当前线程持有,释放锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
Redis实现分布式锁
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.common.exception.CommonException;
import com.demo.common.vo.UserTest;
import com.demo.server.mapper.UserTestMapper;
import com.demo.server.service.UserTestService;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@Service
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class UserServiceImpl extends ServiceImpl<UserTestMapper, UserTest> implements UserTestService {
private final RedisTemplate<String, Object> redisTemplate;
@Override
public int lockTest1(long id) {
// 尝试获取锁
Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("lock:user:" + id, id, 30L, TimeUnit.SECONDS);
if (Objects.equals(ifAbsent, Boolean.TRUE)) {
UserTest byId = baseMapper.selectById(id);
Optional.ofNullable(byId).ifPresent(userTest -> {
if (userTest.getAge() <= 0) {
throw new RuntimeException("不能是负数");
}
});
return baseMapper.lockTest1(id);
}
throw new RuntimeException("获取锁失败");
}
}
简单demo,简单测试。更多使用Redis实现分布式锁了解可以查看该大佬详细文章