背景:新工作的第一个需求要实现分布式的并发控制,跟以往写过的单服务应用不同,synchronized 锁sempheore信号量这种jvm层级的锁就不能满足要求了,要考虑类似阿里开源组件sentinel或者自己实现一个。
要实现分布式下的原子性,就要保证进程在操作竞态资源的时候是唯一的,不能出现并发的情况
redis锁可以满足这种要求,应用之间,能同时拿到redis锁的服务只能有一个,可以保证分布式下某个操作的原子性
对于springboot项目,首先引入redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
增加redis配置,这里redis是安装在本机的,全部都是默认配置
redis:
database: 0
# Redis服务器地址
host: localhost
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 链接超时时间 单位 ms(毫秒)
timeout: 3000
最后是代码层面,主要原理就是使用redis的setIfAbsent命令实现锁的效果,示例代码如下
import java.util.concurrent.TimeUnit;
@Service
public class LockService {
private static final String lockKey = "lockkey";
private static final String lockValue = "lockvalue";
@Autowired
private RedisTemplate redisTemplate;
//获取锁方法
public boolean acquireRedisLock() {
return redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 10, TimeUnit.SECONDS);
}
//释放锁方法
public void releaseRedisLock() {
if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
任何应用,在操作资源之前先获取锁,另一进程尝获取锁时会执行setIfAbsent,此时锁value已经不为空了,所以执行会失败,在锁被释放之前都无法获取
注意点:
超时时间必须设置,且要根据具体业务时间长短来,如果有进程获取锁后宕机而又没有设置过期时间,会产生死锁。如果锁过期时间短于业务时间可能导致锁被提前释放导致错误