文章目录- 一、为什么需要分布式锁?
- 二、分布式锁要满足哪些要求呢?
- 三、分布式锁的实现方式
- 1、引入依赖
- 2、配置类
- 3、配置文件
- 4、RedisOpService(接口)
- 5、RedisOpServiceImpl(实现类)
- 6、RedisController
- 1、引入依赖
- 2、配置类
- 3、配置文件
- 4、RedisOpService(接口)
- 5、RedisOpServiceImpl(实现类)
- 6、RedisController
一、为什么需要分布式锁?
因为Java中的锁,只作用于单个JVM实例上。而当下在互联网技术架构中,大家都用的分布式架构了,应用部署到多个服务器,这种情况下,线程之间的锁机制,就没作用了。为了解决这个问题,我们就引入分布式锁。
二、分布式锁要满足哪些要求呢?
(1)排他性:在同一时间只会有一个客户端能获取到锁,其它客户端无法同时获取。 (2)避免死锁:这把锁在一段有限的时间之后,一定会被释放。 (3)高可用:获取或释放锁的机制必须高可用且性能佳。
三、分布式锁的实现方式
这里只讲基于Redis实现的分布式锁,需要引入Redisson。
1、引入依赖
代码语言:javascript复制<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
2、配置类
代码语言:javascript复制@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String address;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.database}")
private Integer database;
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
address = StringUtils.strip(address.trim(), ",");
String[] addressList = address.split(",");
if (addressList.length == 1) {
config.useSingleServer().setDatabase(database).setAddress("redis://" addressList[0] ":" port);
if (StringUtils.isNotEmpty(password)) {
config.useSingleServer().setPassword(password);
}
} else {
ClusterServersConfig clusterServersConfig = config.useClusterServers().setScanInterval(2000);
for (String address : addressList) {
clusterServersConfig.addNodeAddress("redis://" address ":" port);
if (StringUtils.isNotEmpty(password)) {
clusterServersConfig.setPassword(password);
}
}
}
return Redisson.create(config);
}
}
3、配置文件
代码语言:javascript复制spring:
redis:
host: 127.0.0.1
port: 6379
password:
database: 0 # Redis数据库索引(默认为0)
lettuce:
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制) (默认为8)
max-wait: 0 # 连接池最大阻塞等待时间(使用负值表示没有限制) (默认为-1)
max-idle: 8 # 连接池中的最大空闲连接 (默认为8)
min-idle: 0 # 连接池中的最小空闲连接 (默认为0)
4、RedisOpService(接口)
代码语言:javascript复制public interface RedisOpService {
Boolean isLocked(String lockName);
Boolean lock(String lockName);
Boolean unlock(String lockName);
}
5、RedisOpServiceImpl(实现类)
代码语言:javascript复制@Service
@Slf4j
public class RedisOpServiceImpl implements RedisOpService {
@Resource
private RedissonClient redissonClient;
@Override
public Boolean isLocked(String lockName) {
RLock rLock = redissonClient.getLock(lockName);
return rLock.isLocked();
}
@Override
public Boolean lock(String lockName) {
try {
RLock rLock = redissonClient.getLock(lockName);
// 锁一段时间后自动释放,防止死锁
rLock.lock(10, TimeUnit.SECONDS);
log.info("RedisLock lock [{}] success", lockName);
} catch (Exception e) {
log.error("RedisLock lock [{}] Exception:", lockName, e);
return false;
}
return true;
}
@Override
public Boolean unlock(String lockName) {
try {
RLock rLock = redissonClient.getLock(lockName);
rLock.unlock();
log.info("RedisLock unlock [{}] success", lockName);
} catch (Exception e) {
log.error("RedisLock unlock [{}] Exception:", lockName, e);
return false;
}
return true;
}
}
6、RedisController
代码语言:javascript复制@RestController
@Slf4j
public class RedisController {
@Resource
private RedisOpService redisOpService;
@GetMapping("test")
public void test() {
// 分布式锁处理,情况1:有竞争时,后面的请求报错丢弃,及时响应消息“请稍后重试”
String lockName = "test:" 666;
if (redisOpService.isLocked(lockName)) {
throw new RuntimeException("请稍后重试");
}
try {
if (redisOpService.lock(lockName)) {
log.info("执行业务逻辑");
}
} catch (Exception e) {
log.error("异常", e);
} finally {
redisOpService.unlock(lockName);
}
}
@GetMapping("test2")
public void test2() {
// 分布式锁处理,情况2:有竞争时,阻塞等待,依次获取锁->执行->释放锁,直到都处理完成
String lockName = "test:" 777;
try {
if (redisOpService.lock(lockName)) {
log.info("执行业务逻辑");
}
} catch (Exception e) {
log.error("异常", e);
} finally {
redisOpService.unlock(lockName);
}
}
}
两种应用场景:
- 情况1:有竞争时,后面的请求报错丢弃,及时响应消息“请稍后重试”;
- 情况2:有竞争时,阻塞等待,依次获取锁->执行->释放锁,直到都处理完成。