基于Redis分布式锁Redisson及SpringBoot集成Redisson
短信预约 -IT技能 免费直播动态提醒
- 分布式锁需要具备的条件和刚需
- 独占性:OnlyOne,任何时刻只能有且仅有一个线程持有
- 高可用:若redis集群环境下,不能因为某一个节点挂了而出现获取锁和释放锁失败的情况
- 防死锁:杜绝死锁,必须有超时控制机制或者撤销操作,有个兜底终止跳出方案
- 不乱抢:防止张冠李戴,不能私下unlock别人的锁,只能自己加锁自己释放
- 重入性:同一个节点的同一个线程如果获得锁之后,它也可以再次获取这个锁
- Redisson使用
引入 redisson 依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.2</version>
</dependency>
添加 redisson 配置:
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
// 设置key序列号方式string
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 设置value的序列化方式json
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public Redisson redisson() {
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.10.233:6379").setDatabase(0);
return (Redisson) Redisson.create(config);
}
}
接口测试:
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private Redisson redisson;
private static final String KEY = "spike";
@GetMapping("/buy")
public String bugGood() {
RLock redissonLock = redisson.getLock(KEY);
redissonLock.lock();
try {
String result = stringRedisTemplate.opsForValue().get("goods:001");
int goodNumber = result == null ? 0 : Integer.parseInt(result);
if (goodNumber > 0) {
int realNum = goodNumber - 1;
stringRedisTemplate.opsForValue().set("goods:001", realNum + "");
return "秒杀成功,剩余库存:" + realNum;
}
return "商品已售罄!";
} finally {
redissonLock.unlock();
}
}
}
多节点的情况下,代码相同,要保证 lock 的 key 一样
- SpringBoot集成Redisson
引入 redis 和 redisson starter:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
redis 配置:
spring:
redis:
# 地址
host: 192.168.20.26
# 端口,默认为6379
port: 6379
password: 123456
# 连接超时时间
timeout: 30s
database: 0
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
redisson配置:
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.redisson.config.TransportMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.setTransportMode(TransportMode.NIO);
// 单体配置,如果是SSL连接,使用 rediss://
SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress("redis://" + host + ":" + port);
singleServerConfig.setPassword(password);
return Redisson.create(config);
}
}
使用:
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
public class TestController {
@Resource
private RedissonClient redissonClient;
public void doAction1() {
String lockKey = "lockKey";
RLock lock = redissonClient.getLock(lockKey);
// 加锁,无参数
lock.lock();
// 锁有效时间,时间单位
// lock.lock(5, TimeUnit.SECONDS);
try {
// do something
} finally {
// 解锁
lock.unlock();
}
}
public void doAction2() throws InterruptedException {
String lockKey = "lockKey";
RLock lock = redissonClient.getLock(lockKey);
// 1. 无参数,直接上锁
// lock.tryLock();
// 2. 等待时间,时间单位
// lock.tryLock(3, TimeUnit.SECONDS);
// 3. 等待时间,锁有效时间,时间单位
// lock.tryLock(3, 5, TimeUnit.SECONDS);
try {
// 常规写法
if (lock.tryLock(3, 5, TimeUnit.SECONDS)) {
// do something
} else {
// 没有获取到锁
}
} finally {
// 解锁
lock.unlock();
}
}
}
到此这篇关于基于Redis分布式锁Redisson及SpringBoot集成Redisson的文章就介绍到这了,更多相关Redis分布式锁内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341