本文是向大家介绍项目中如何快速使用redis实现分布式锁,落地实现步骤。
简介
实现分布式锁有多种方式,常见redis,zookeeper,数据库等多种方式实现,由于redis实现相对简单快捷,所以本次分享聚焦redis分布式锁实现方式,下面我们介绍下在我们实际项目已经正式落地的实现方式。具体方案见如下。
方案1. spring-integration中redis分布锁
spring-integration对redis分布锁的支持,底层应该也是lua脚本的实现,可完美解决线程挂掉造成的死 锁,以及执行时间过长锁释放掉,误删别人的锁。不适用redis集群,(可用于阿里云redis,阿里云redis相当于单机)
使用步骤:
第一步:引入jar
<!-- redis分布式锁 begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
</dependency>
<!-- redis分布式锁 end-->
第二步:配置redisLockRegistry
@Configuration
public class RedisLockConfiguration {
@Bean
public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
return new RedisLockRegistry(redisConnectionFactory, "mfbizcore:redis_lock");
}
}
第三步:配置文件
#Redis服务器地址
spring.redis.host=127.0.1.1
#Redis服务器连接密码
spring.redis.password=123456
#Redis服务器连接端口
spring.redis.port=30079
spring.redis.database=1
#连接池最大连接数
spring.redis.pool.maxActive=8
#连接池最大阻塞等待时间
spring.redis.pool.maxWait=-1
#连接池中的最大空闲连接
spring.redis.pool.maxIdle=8
#连接池中的最小空闲连接
spring.redis.pool.minIdle=0
#连接超时时间(毫秒)
spring.redis.timeout=10000
第四步:分布式锁使用示例
@Autowired
private RedisLockRegistry redisLockRegistry;
// 省略
Lock lock = redisLockRegistry.obtain(redisLockKey);
if(!lock.tryLock()){
//加锁失败
LOGGER.info("当前用户:{},已有扣款流程正在处理中,redis锁key{}",userId,redisLockKey);
return false;
}
try{
// 省略业务
}finally{
if (lock != null) {
//释放锁
lock.unlock();
LOGGER.info("当前用户:{},扣款流程结束,进行锁释放操作,redis锁key:{}",userId,redisLockKey);
}
}
方案2. Redission分布式锁 (可用于集群)
Redission是Redis官方推荐的客户端,提供了一个RLock的锁,RLock继承自juc的Lock接口,提供了中断,超时,尝试获取锁等操作,支持可重入,互斥等特性。
使用步骤:
第一步:引入依赖包
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.11.2</version>
</dependency>
第二步:配置redission
@Configuration
public class RedissonConfig {
@Autowired
private RedisConfigProperties redisConfigProperties;
//添加redisson的bean
@Bean
public Redisson redisson() {
//redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加
List<String> clusterNodes = new ArrayList<>();
for (int i = 0; i < redisConfigProperties.getCluster().getNodes().size(); i++) {
clusterNodes.add("redis://" + redisConfigProperties.getCluster().getNodes().get(i));
}
Config config = new Config();
ClusterServersConfig clusterServersConfig = config.useClusterServers()
.addNodeAddress(clusterNodes.toArray(new String[clusterNodes.size()]));
clusterServersConfig.setPassword(redisConfigProperties.getPassword());//设置密码
return (Redisson) Redisson.create(config);
}
}
@Component
@ConfigurationProperties(prefix = "spring.redis")
public class RedisConfigProperties {
private String password;
private cluster cluster;
public static class cluster {
private List<String> nodes;
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public RedisConfigProperties.cluster getCluster() {
return cluster;
}
public void setCluster(RedisConfigProperties.cluster cluster) {
this.cluster = cluster;
}
}
第三步:使用示例
@Autowired
private Redisson redisson;
@GetMapping("/test4")
public void test4() {
String stockKey = "product_1001";
RLock lock = redisson.getLock(stockKey);
try {
// 加锁
lock.lock();
int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock -1;
redisTemplate.opsForValue().set("stock", String.valueOf(realStock));
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
} finally {
// 释放锁
lock.unlock();
}
}