springboot 集成redission 以及分布式锁的使用详解

目录
  • springboot集成redission及分布式锁的使用
    • 1、引入jar包
    • 2、增加Configuration类
    • 3、使用redission分布式锁
  • Springboot整合Redisson 锁
    • 一、依赖
    • 二、配置文件
    • 三、锁的使用
    • 四、分布式秒杀
    • 五、redis锁 单机版可用,分布式用Redisson

springboot集成redission及分布式锁的使用

1、引入jar包

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.4</version>
</dependency>

2、增加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 getRedisson() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
        return Redisson.create(config);
    }
}

3、使用redission分布式锁

@Autowired
private RedissonClient redissonClient;
 
//方法区
String key = "aa:bb:cc:01";
RLock rLock =redissonClient.getLock(key);
try{<br>// 尝试加锁,最多等待1秒,上锁以后10秒自动解锁<br>// 没有Watch Dog ,10s后自动释放
boolean res = rLock.tryLock(1,10, TimeUnit.SECONDS);
if(!res){
  return new GeneralVO<>(400, "请勿重复提交", false);
}
}finally{
    rLock.unlock();
}
private void redissonDoc() throws InterruptedException {
    //1. 普通的可重入锁
    RLock lock = redissonClient.getLock("generalLock");

    // 拿锁失败时会不停的重试
    // 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30s
    lock.lock();

    // 尝试拿锁10s后停止重试,返回false
    // 具有Watch Dog 自动延期机制 默认续30s
    boolean res1 = lock.tryLock(10, TimeUnit.SECONDS);

    // 拿锁失败时会不停的重试
    // 没有Watch Dog ,10s后自动释放
    lock.lock(10, TimeUnit.SECONDS);

    // 尝试拿锁100s后停止重试,返回false
    // 没有Watch Dog ,10s后自动释放
    boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS);

    //2. 公平锁 保证 Redisson 客户端线程将以其请求的顺序获得锁
    RLock fairLock = redissonClient.getFairLock("fairLock");

    //3. 读写锁 没错与JDK中ReentrantLock的读写锁效果一样
    RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("readWriteLock");
    readWriteLock.readLock().lock();
    readWriteLock.writeLock().lock();
}

Springboot整合Redisson 锁

Redisson是一个在Redis的基础上实现的Java驻内存数据网格

一、依赖

  <dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.15.4</version>
  </dependency>

二、配置文件

spring:
  redis:
    database: 7
    host: 116.62.178.11
    port: 6379
    password: 1234qwer
    #  spring-boot 1.0默认 jedis;  spring-boot2.0 默认lettuce ,lettuce线程安全
    lettuce:
      pool:
        # 连接池中的最大空闲连接 默认8
        max-idle: 8
        # 连接池中的最小空闲连接 默认0
        min-idle: 500
        # 连接池最大连接数 默认8 ,负数表示没有限制
        max-active: 2000
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1
        max-wait: -1
    cache:
      type: redis
@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;
    @Bean(destroyMethod = "shutdown")
    RedissonClient redissonClient() throws IOException {
        Config config = new Config();
        config.useSingleServer()
                .setPassword(password)
                .setAddress("redis://" + host + ":" + port).setDatabase(7);
        return Redisson.create(config);
    }
}

三、锁的使用

读写锁

public class RedissionDemo {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 读写锁 总结
     *
     * 读锁又叫共享锁
     * 写锁又叫排他锁(互斥锁)
     * 读 + 读 相当于无锁,并发读,同时加锁成功
     * 写 + 写 阻塞状态
     * 写 + 读 等待写锁释放
     * 读 + 写 等待读锁完,才写,
     */
    public String writeValue() {
        String str = "";
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("writeLock");
        RLock rLock = readWriteLock.writeLock();
        try {
            rLock.lock();
            str = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set("uuid", str);
            Thread.sleep(30000);
        } catch (Exception e) {
        } finally {
            rLock.unlock();
        }
        return str;
    }
    /**
     * 读锁
     *
     * @return
     */
    public String readValue() {
        String str = "";
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("writeLock");
        RLock rLock = readWriteLock.readLock();
        rLock.lock();
        str = (String) redisTemplate.opsForValue().get("uuid");
        rLock.unlock();
        return str;
    }

}

信号量

public class RedissionDemo {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 信号量
     *
     * @return
     */
    //停车方法
    @GetMapping("/park")
    public String park() throws InterruptedException {
        //这里是获取信号量的值,这个信号量的name一定要与你初始化的一致
        RSemaphore park = redissonClient.getSemaphore("park");
        //这里会将信号量里面的值-1,如果为0则一直等待,直到信号量>0
        park.acquire();
        //tryAcquire为非阻塞式等待
        //park.tryAcquire();
        return "ok";
    }
    public String go() throws InterruptedException {
        //这里是获取信号量的值,这个信号量的name一定要与你初始化的一致
        RSemaphore park = redissonClient.getSemaphore("park");
        //这里会将信号量里面的值+1,也就是释放信号量
        park.release();
        return "ok";
    }
}

闭锁

public class RedissionDemo {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 闭锁,限流
     *
     * @return
     * @throws InterruptedException
     */
    //锁门
    public String lockdoor() throws InterruptedException {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        //设置一个班级有20个同学
        door.trySetCount(20);
        //需要等到20个同学全部离开,才锁门
        door.await();
        return "锁门了";
    }
    public String leave(Long id) throws InterruptedException {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        //表示一个同学离开
        door.countDown();
        return "" + id + "号同学离开了";
    }
}

四、分布式秒杀

秒杀流程:

@Service
@Slf4j
public class DistributedSecKillBiz {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 分布式锁。唯一缺点 枷锁失效时间
     * 枷锁院子操作,
     * 解锁,删除锁也是原子操作 瑕疵没有续命
     *
     * @return
     */
    public String doKill() {
        String lock = UUID.randomUUID().toString();
        String goodsId = "10054";
        boolean flag = redisTemplate.opsForValue().setIfAbsent(goodsId, lock, 30, TimeUnit.SECONDS);
        if (flag) {
            // 获取锁成功
            try {
                Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
                if (stock > 0) {
                    redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
                    log.info("扣减库存成功,还剩:" + stock);
                }
                return "库存不足,该商品已抢购完!";
            } catch (Exception e) {
            } finally {
                String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(goodsId), lock);
            }
        }
        return doKill();
    }
    /**
     * 整合 redission
     * @return
     */
    public String doKillDistributed() {
        String goodsId = "10054";
        RLock lock = redissonClient.getLock(upActivityKey() + SecKillConstant.LOCK + goodsId);
        // 获取锁成功
        try {
            //1 阻塞式等待,默认30秒时间
            //2 自动续期,如果业务超长,续上新的30秒,不用担心过期时间,锁自动删除掉
            //3 枷锁的业务运行完成,就不会给当前的锁自动续期,即使没有手动释放锁也会,30秒自动释放
//            lock.lock(30, TimeUnit.SECONDS); //不会自动续期需要注意
            lock.lock();
            Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
            if (stock > 0) {
                redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
                log.info("扣减库存成功,还剩:" + stock);
            }
            return "库存不足,该商品已抢购完!";
        } catch (Exception e) {
        } finally {
            lock.unlock();
        }
        return "fail";
    }
    /**
     * 获取活动
     *
     * @return
     */
    public ActivityBo upActivity() {
        return new ActivityBo("七夕活动", "SEVEN_ACTIVITY", new Date(), new Date());
    }
    /**
     * 活动公共key
     *
     * @return
     */
    public String upActivityKey() {
        return SecKillConstant.SEC_KILL + upActivity().getActivityKey() + ":";
    }
}

五、redis锁 单机版可用,分布式用Redisson

package com.yang.yimall.seckill.app.seckill.biz;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
 * redis 锁 集群有瑕疵 不能 续命
 */
@Service
public class RedisLock {
    @Autowired
    private RedisTemplate redisTemplate;
    private String lockName = "lockName";
    private ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public void lock(String lockName) {
        if (tryLock(lockName)) {
            return;
        }
        lock(lockName);
    }
    public void lock() {
        if (tryLock(lockName)) {
            return;
        }
        lock();
    }
    /**
     * 添加key 并且设置过期时间 原子操作
     *
     * @param lockName
     * @return
     */
    public boolean tryLock(String lockName) {
        String uuid = UUID.randomUUID().toString();
        threadLocal.set(uuid);
        return redisTemplate.opsForValue().setIfAbsent(lockName, uuid, 30, TimeUnit.SECONDS);
    }
    /**
     * 如果查询有key,就删除, 原子操作
     */
    public void unlock() {
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Collections.singletonList(lockName), threadLocal.get());
    }
}

使用

 public String doKillUp() {
        String goodsId = "10054";
        redisLock.lock(goodsId);
        // 获取锁成功
        try {
            Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
            if (stock > 0) {
                redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
                log.info("扣减库存成功,还剩:" + stock);
            }
            return "库存不足,该商品已抢购完!";
        } catch (Exception e) {
        } finally {
            redisLock.unlock();
        }
        return "库存不足,该商品已抢购完!";
    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • SpringBoot使用Redisson实现分布式锁(秒杀系统)

    前面讲完了Redis的分布式锁的实现,接下来讲Redisson的分布式锁的实现,一般提及到Redis的分布式锁我们更多的使用的是Redisson的分布式锁,Redis的官方也是建议我们这样去做的.Redisson点我可以直接跳转到Redisson的官方文档. 1.1.引入Maven依赖 <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter&l

  • SpringBoot集成Redisson实现分布式锁的方法示例

    上篇 <SpringBoot 集成 redis 分布式锁优化>对死锁的问题进行了优化,今天介绍的是 redis 官方推荐使用的 Redisson ,Redisson 架设在 redis 基础上的 Java 驻内存数据网格(In-Memory Data Grid),基于NIO的 Netty 框架上,利用了 redis 键值数据库.功能非常强大,解决了很多分布式架构中的问题. Github的wiki地址: https://github.com/redisson/redisson/wiki 官方文档

  • SpringBoot整合Redis正确的实现分布式锁的示例代码

    前言 最近在做分块上传的业务,使用到了Redis来维护上传过程中的分块编号. 每上传完成一个分块就获取一下文件的分块集合,加入新上传的编号,手动接口测试下是没有问题的,前端通过并发上传调用就出现问题了,并发的get再set,就会存在覆盖写现象,导致最后的分块数据不对,不能触发分块合并请求. 遇到并发二话不说先上锁,针对执行代码块加了一个JVM锁之后问题就解决了. 仔细一想还是不太对,项目是分布式部署的,做了负载均衡,一个节点的代码被锁住了,请求轮询到其他节点还是可以进行覆盖写,并没有解决到问题啊

  • SpringBoot中使用redis做分布式锁的方法

    一.模拟问题 最近在公司遇到一个问题,挂号系统是做的集群,比如启动了两个相同的服务,病人挂号的时候可能会出现同号的情况,比如两个病人挂出来的号都是上午2号.这就出现了问题,由于是集群部署的,所以单纯在代码中的方法中加锁是不能解决这种情况的.下面我将模拟这种情况,用redis做分布式锁来解决这个问题. 1.新建挂号明细表 2.在idea上新建项目 下图是创建好的项目结构,上面那个parent项目是其他项目不用管它,和新建的没有关系 3.开始创建controller,service,dao(mapp

  • springboot 集成redission 以及分布式锁的使用详解

    目录 springboot集成redission及分布式锁的使用 1.引入jar包 2.增加Configuration类 3.使用redission分布式锁 Springboot整合Redisson 锁 一.依赖 二.配置文件 三.锁的使用 四.分布式秒杀 五.redis锁 单机版可用,分布式用Redisson springboot集成redission及分布式锁的使用 1.引入jar包 <dependency> <groupId>org.redisson</groupId&

  • SpringBoot集成redis实现分布式锁的示例代码

    1.准备 使用redis实现分布式锁,需要用的setnx(),所以需要集成Jedis 需要引入jar,jar最好和redis的jar版本对应上,不然会出现版本冲突,使用的时候会报异常redis.clients.jedis.Jedis.set(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String; 我使用的redis版本是2.3.0,Jedis使用的是3.3.0 <de

  • SpringBoot集成本地缓存性能之王Caffeine示例详解

    目录 引言 Spring Cache 是什么 集成 Caffeine 核心原理 引言 使用缓存的目的就是提高性能,今天码哥带大家实践运用 spring-boot-starter-cache 抽象的缓存组件去集成本地缓存性能之王 Caffeine. 大家需要注意的是:in-memeory 缓存只适合在单体应用,不适合与分布式环境. 分布式环境的情况下需要将缓存修改同步到每个节点,需要一个同步机制保证每个节点缓存数据最终一致. Spring Cache 是什么 不使用 Spring Cache 抽象

  • Spring-boot集成pg、mongo多数据源过程详解

    这篇文章主要介绍了Spring-boot集成pg.mongo多数据源过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 修改POM文件,增加相应Jar包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </

  • SpringBoot集成mqtt的多模块项目配置详解

    前言 近期为了准备毕设,准备使用SpringBoot搭建mqtt后端,本篇主要记录了在IDEA中搭建SpringBoot mqtt的多模块项目的过程 开发工具及系统环境 IDE:IntelliJ IDEA 2020.2 操作系统:Windows 10 2004 Java Version:1.8 SpringBoot Version:2.1.17.RELEASE 项目路径 Study |----study-common # 存放公共类 |----study-mapper # mapper层 |--

  • SpringBoot集成JWT实现登陆验证的方法详解

    1:首先,我们需要在项目中导入两个依赖: <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifa

  • SpringBoot集成P6Spy实现SQL日志的记录详解

    目录 P6Spy简介 应用场景 pom application.yml entity Mapper 启动类 测试类 P6Spy入门使用 spy.properties P6SPYConfig application.yml P6Spy简介 P6Spy是一个可以用来在应用程序中拦截和修改数据操作语句的开源框架. 通过P6Spy可以对SQL语句进行拦截,相当于一个SQL语句的记录器,这样我们可以用它来作相关的分析,比如性能分析. 应用场景 pom <dependencies> <depende

  • SpringBoot 集成 ShedLock 分布式锁的示例详解

    目录 一:ShedLock 简介 二:配置maven 依赖 三:SchedulerLock 基于 Redis 的配置 四:在启动类中添加 @EnableScheduling 标签 五:test 测试案例 一:ShedLock 简介 ShedLock是一个在分布式环境中使用的定时任务框架,用于解决在分布式环境中的多个实例的相同定时任务在同一时间点重复执行的问题.ShedLock确保计划的任务最多同时执行一次.如果一个任务正在一个节点上执行,它会获得一个锁,该锁将阻止从另一个节点(或线程)执行同一任

  • springboot集成spark并使用spark-sql的示例详解

    首先添加相关依赖: <?xml version="1.0" encoding="UTF-8"?> <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

随机推荐