Redisson实现分布式锁

  业务场景:商品秒杀减库存操作

 

一、先简单创一个springBoot项目

 

 

 

 

 

 pom.xml加入依赖

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.dengfeng</groupId>
    <artifactId>webprojectDemo-springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>    
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.6.5</version>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>


    </dependencies>
</project>

 

创建配置文件application.yml

server:
  port: 8100

 

新建2个类,IndexController,和启动类StartApplication

 

 

StartApplication

package com.dengfeng.lock;



import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;



@SpringBootApplication
public class StartApplication {

    public static void main(String[] args) {
        SpringApplication.run(StartApplication.class, args);
    }
    
    //实例化一个redisson客户端,redisson框架主要解决分布式锁时间设置问题
    @Bean
    public Redisson redisson() {
        Config config=new Config();
        config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);
        return (Redisson)Redisson.create(config);
    }

}

 

IndexController

package com.dengfeng.lock;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/*
 * 使用redisson实现分布式锁
 */
@RestController
public class IndexController {
    
    private static final Logger logger = LoggerFactory.getLogger(IndexController.class);
    
    @Autowired
    private Redisson redisson;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @RequestMapping("/getStock")
    public String deductStock() {
        String lockKey="lockKey";        
        RLock redissonLock = redisson.getLock(lockKey);
        int stock;
        try {    
            //synchronized锁是锁在jvm,集群环境下不适用,redisson锁在redis,redis底层是单线程
            //加锁redissonLock.lock,底层设置了锁,默认时间30秒,stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "dengfeng",30, TimeUnit.SECONDS); =jedis.setnx(key,value);无key添加成功,有key不能添加              
            //底层开启了定时续命分线程。加锁后,只有一条线程能执行以下业务代码,其他线程会等待该线程释放资源,会在当前位置阻塞
            //分线程每隔10秒检查是否还持有锁,如果持有则延长锁的时间,间隔10秒不是固定,取锁时间的1/3
            //底层用lua脚本判断是否有相同key,锁时间,加锁其实就是设置key,value
            redissonLock.lock();
            
            //高并发多条线程进来时,
            stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
        
            if(stock>0) {
                int realStock=stock-1;
                stringRedisTemplate.opsForValue().set("stock", realStock+"");
                System.out.println("redis数据库扣减成功:库存剩余"+realStock);
            }else {
                System.out.println("库存不足,扣减失败");
            }                    
        } finally{        
            redissonLock.unlock();             //释放锁
        }
        
        return String.valueOf(stock);
    }
    
    
    
    @RequestMapping("/dengfeng")
    public String deductStock1() {
        
        return "dengfeng123456";
    }

}

 

以上代码加锁完成,下面是原理图和底层代码

 

加锁原理图如下

 

底层采用lua脚本将字符串返回给redis,使用redis原子性,要么都成功,要么都不成功 

 

附:AOP原理加锁

  为了不影响原有业务和代码冗余等,我想通过注解+AOP使用redisson加锁,在每个接口上通过如下注解

      参考文章:https://www.cnblogs.com/qq1728209643/p/10050191.html

posted @ 2021-02-22 14:19  登风360  阅读(95)  评论(0编辑  收藏  举报