• 基于redis的分布式锁实现方案


    基于redis的分布式锁(redisson)

    分布式锁介绍:

           我们在开发应用时,如果需要对一个共享变量进行多线程同步访问的时候,我们可以使用Java多线程的各个技能点来处理,保证完美运行无BUG。
    但是这里的都只是单机应用,即在同一个JVM中;然后随着业务发展、微服务化,一个应用需要部署到多台服务器上然后做负载均衡,大概的架构图如下:

             在上图可以看到,变量A在JVM1、JVM2、JVM3三个JVM内存中(这个变量A主要体现是在一个类中的一个成员变量,是一个有状态的对象),如果我们不加任何控制的话,变量A同进都会在JVM分配一块内存,三个请求发过来同时对这个变量进行操作,显然结果不是我们想要的。

    如果我们业务中存在这样的场景的话,就需要找到一种方法来解决。

    为了保证一个方法或属性在高并发的情况下同一时间只能被同一个线程执行,在传统单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLockSynchronized)进行互斥控制。但是,随之业务发展的需要,原单机部署的系统演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同的机器上,这将原来的单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。
    为了解决这个问题,就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

    分布式锁应该具备哪些条件

    • 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
    • 高可用、高性能的获取锁与释放锁;
    • 具备可重入特性;
    • 具备锁失效机制、防止死锁;
    • 具备非阻塞锁特性,即没有获取到锁直接返回获取锁失败;

    分布式锁的实现方式

         目前几乎所有大型网站及应用都是分布式部署,分布式场景中的数据一致性问题一直是一个比较重要的话题,分布式的CAP理论告诉我们任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项
    一般情况下,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证最终一致性,只要这个最终时间是在用户可以接受的范围内即可。
    在很多时候,为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一信方法在同一时间内只能被同一个线程执行。

    关于分布式锁的实现,目前主流方案有以下三类:

    1、基于数据库的乐观锁;

    2、基于redis实现的锁服务;

    3、基于zookeeper的实现;

    这里主要介绍基于Redis的分布式锁的实现:

    引入依赖:

    <dependency>
         <groupId>org.redisson</groupId>
         <artifactId>redisson-spring-boot-starter</artifactId>
         <version>3.10.1</version>
     </dependency>
       <!--redis-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

    封装工具类:

    package com.dw.study.util;
    
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Author dw
     * @ClassName RedissionLockUtil
     * @Description 基于Redisson的分布式锁实现
     * @Date 2020/5/24 0:36
     * @Version 1.0
     */

    @componet public class RedissionLockUtil { @Autowired private RedissonClient redissonClient; /** * 加锁 * @param lockKey * @return */ public RLock lock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); lock.lock(); return lock; } /** * 加锁,过期自动释放 * @param lockKey * @param leaseTime 自动释放锁时间 * @return */ public RLock lock(String lockKey, long leaseTime) { RLock lock = redissonClient.getLock(lockKey); lock.lock(leaseTime, TimeUnit.SECONDS); return lock; } /** * 加锁,过期自动释放,时间单位传入 * @param lockKey * @param unit 时间单位 * @param leaseTime 上锁后自动释放时间 * @return */ public RLock lock(String lockKey, TimeUnit unit, long leaseTime) { RLock lock = redissonClient.getLock(lockKey); lock.lock(leaseTime, unit); return lock; } /** * 尝试获取锁 * @param lockKey * @param unit 时间单位 * @param waitTime 最多等待时间 * @param leaseTime 上锁后自动释放时间 * @return */ public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) { RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) { return false; } } /** * 尝试获取锁 * @param lockKey * @param waitTime 最多等待时间 * @param leaseTime 上锁后自动释放锁时间 * @return */ public boolean tryLock(String lockKey, long waitTime, long leaseTime) { RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); } 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(); } }

    二、使用方式

     @Autowired
        private RedissionLockUtil redissionLockUtil;
    
        String lockKey = "only_id";
         try{
            //超过2S自动释放锁
            redissionLockUtil.lock(lockKey, 2L);
            //业务处理
    
        } finally{
            redissionLockUtil.unlock(lockKey);  //释放锁
        }
  • 相关阅读:
    h5页面页面在iphoneX手机上底部会有留白解决办法
    自定义单张图片放大预览功能,可支持手势缩放,依赖jquery
    js事件内部有click事件时,click事件会重复调用解决方法
    h5页面通过阿里云的broswer-js-sdk上传文件
    python字符串前加r、f、u、l 的区别
    Python基础面试题 :计算列表中出现最多次的字符
    python基础入门教程:传参是传值还是传引用
    Python 面试题:输入一个数组,输出该数组的第二大的数字
    Python 7种超实用的数据清洗方法,这你一定要掌握
    python教程:3个非常有用的内置函数(filter/map/reduce)
  • 原文地址:https://www.cnblogs.com/dw3306/p/12945368.html
Copyright © 2020-2023  润新知