阿里大牛详细讲解:Spring Boot 集成 Redisson 实现分布式锁
针对单机分布式锁还是存在锁定续期、可重入的问题,本文将采用 Spring Boot 集成 Ression 实现分布式锁进行详细讲解。
分布式锁实现
引入 jar 包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.13.6</version> </dependency>
说明:关于集成 Redisson,我们需要注意与 Spring Boot 的版本对应。具体对应的关系如下:
注意:3.13.6 对应的 Spring Boot 的版本为 2.3.0,而 redis-spring-data 为 redis-spring-data-23。我们可以通过查看 pom 文件的引用从而得到依赖关系。
Redisson 的配置
application.yml 中引入 redisson.yml 配置
redis: redisson: file: classpath:redisson.yml
redisson.yml 配置
singleServerConfig: password: xxxx address: "redis://127.0.0.1:6379" database: 1threads: 0nettyThreads: 0codec: !<org.redisson.codec.FstCodec> {}transportMode: "NIO"
说明:本文配置的是单机环境,如果需要配置集群环境,可以采用如下配置:
clusterServersConfig: idleConnectionTimeout: 10000 connectTimeout: 10000 timeout: 3000 retryAttempts: 3 retryInterval: 1500 failedSlaveReconnectionInterval: 3000 failedSlaveCheckInterval: 60000 password: null subscriptionsPerConnection: 5 clientName: null loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {} subscriptionConnectionMinimumIdleSize: 1 subscriptionConnectionPoolSize: 50 slaveConnectionMinimumIdleSize: 24 slaveConnectionPoolSize: 64 masterConnectionMinimumIdleSize: 24 masterConnectionPoolSize: 64 readMode: "SLAVE" subscriptionMode: "SLAVE" nodeAddresses: - "redis://127.0.0.1:6379" - "redis://127.0.0.1:6380" - "redis://127.0.0.1:6381" scanInterval: 1000 pingConnectionInterval: 0 keepAlive: false tcpNoDelay: false threads: 6 nettyThreads: 12 codec: !<org.redisson.codec.MarshallingCodec> {} transportMode: "NIO"
封装 Redisson 工具类
@Componentpublic class RedissonLockUtil{
private static final Logger logger = LoggerFactory.getLogger(RedissonLockUtil.class); @Autowired private RedissonClient redissonClient; /** * 加锁 * @param lockKey * @return */ public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey); return lock; } /** * 公平锁 * @param key * @return */ public RLock fairLock(String key) {
return redissonClient.getFairLock(key); } /** * 带超时的锁 * @param lockKey * @param timeout 超时时间 单位:秒 */ public RLock lock(String lockKey, int timeout) {
RLock lock = redissonClient.getLock(lockKey); lock.lock(timeout, TimeUnit.SECONDS); return lock; } /** * 读写锁 * @param key * @return */ public RReadWriteLock readWriteLock(String key) {
return redissonClient.getReadWriteLock(key); } /** * 带超时的锁 * @param lockKey * @param unit 时间单位 * @param timeout 超时时间 */ public RLock lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock = redissonClient.getLock(lockKey); lock.lock(timeout, unit); return lock; } /** * 加锁 * @param key * @param supplier * @return */ public <T> T lock(String key, Supplier<T> supplier) {
RLock lock = lock(key); try {
lock.lock(); return supplier.get(); } finally {
if (lock != null && lock.isLocked()) {
lock.unlock(); } } }
/** * 尝试获取锁 * @param lockKey * @param waitTime 等待时间 * @param leaseTime 自动释放锁时间 * @return */ public boolean tryLock(String lockKey, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey); try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); } catch (InterruptedException e) {
return false; } } /** * 尝试获取锁 * @param lockKey * @param unit 时间单位 * @param waitTime 等待时间 * @param leaseTime 自动释放锁时间 * @return */ public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey); try {
return lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) {
return false; } } /** * 释放锁 * @param lockKey */ public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey); lock.unlock(); } /** * 释放锁 * @param lock */ public void unlock(RLock lock) {
lock.unlock(); } }
模拟秒杀扣减库存
public int lockStock() {
String lockKey="lock:stock"; String clientId = UUID.randomUUID().toString(); //加锁 RLock lock=redissonLockUtil.lock(lockKey); lock.lock(); try {
logger.info("加锁成功 clientId:{}",clientId); int stockNum= Integer.valueOf((String)redisUtil.get("seckill:goods:stock")); if(stockNum>0) { stockNum--; redisUtil.set("seckill:goods:stock",String.valueOf(stockNum)); logger.info("秒杀成功,剩余库存:{}",stockNum); } else {
logger.error("秒杀失败,剩余库存:{}", stockNum); } //获取库存数量 return stockNum; } catch (Exception e) {
logger.error("decry stock eror",e); } finally {
if(lock!=null) {
lock.unlock(); } } return 0; }
测试代码
@RequestMapping("/redisLockTest") public void redisLockTest() {
// 初始化秒杀库存数量 redisUtil.set("seckill:goods:stock", "10"); List<Future> futureList = new ArrayList<>(); //多线程异步执行 ExecutorService executors = Executors.newScheduledThreadPool(10); // for (int i = 0; i < 30; i++) {
futureList.add(executors.submit(this::lockStock)); try {
Thread.sleep(100); } catch (InterruptedException e) {
logger.error("redisLockTest error",e); } } // 等待结果,防止主线程退出 futureList.forEach(t -> {
try {
int stockNum =(int) t.get(); logger.info("库存剩余数量:{}",stockNum); } catch (Exception e) {
logger.error("get stock num error",e); } }); }
执行结果如下:
总结
本文针对 Spring Boot 集成 Redisson 的基本使用,关于 Redisson 源码的分析将在后续的文章中进行讲解,如有疑问,请随时反馈。
还没有评论,来说两句吧...