zoukankan      html  css  js  c++  java
  • 初探redis分布式锁

    有这样一个需求,一个系统部署在两套服务器上访问同一个数据库,访问某个表单时需要确定没有没有其他请求在访问,如果有其他请求在访问,则给出相关提示,退出请求。

    这里如果是同一个系统上的请求,可以利用加锁的机制进行同步控制,显然在两个系统下这点是无法做到的,同步就是不同的执行过程在操作同一资源时做的一些控制,比如谁可以操作这个资源就需要先拿到这个资源的访问权,如下图示:

    根据同步控制的思想,设计一个简单的分布式锁方案,既然系统A和系统B都要访问公共资源,能不能再设定一个公共的中间变量,系统A、B在访问公共资源之前先查看这个公共的中间变量的状态,如果这个公共中间变量的状态符合进一步获取公共资源的条件,则进行公共资源的操作,反之不能操作公共资源;根据以上思想,这里设计一个最简单的同步访问控制功能,还有很大的修改余地,但是为我们进一步学习打开了一个细缝,redis可以为我们提供什么帮助呢?

    redis的string类型数据方法中有一个操作:

    set key value ex nseconds nx:如果key不存在,则将key设置为value,同时设置超时时间为nseconds秒;如果key存在,则不进行操作

    根据这个特性设定一个key值,如果key值不存在,则可以取到公共资源,同时给key赋值;这时候其他请求在访问资源之前给key赋值失败,则不能访问资源;待资源访问完毕,再将key值删除,则其他请求可以访问资源了;删除key的时候也不能乱删,需要根据value进行判断删除,以免A的请求结束后将B的key值删掉了,为保证操作原子性,可用LUA脚本进行删除操作:

    if redis.call('get',KEYS[1])==ARGV[1] then

      return redis.call('del',KEYS[1])

    else

      return 0

    end

    根据以上方案,实现如下:

    1、引入pom redis依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <version>2.1.14.RELEASE</version>
    </dependency> 

    2、封装redis锁

    @Component
    public class RedisLockUtil {
        @Autowired
        private RedisTemplate<String, Serializable> redisTemplate;
    
        //删除脚本,判断要删除的val是指定的val后删除key值
        private final String RELEASE_LOCK_SCRIPT="if redis.call('get',KEYS[1]) == ARGV[1] " +
                "then return redis.call('del',KEYS[1]) else return 0 end";
    
        private final DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_SCRIPT,Long.class);
        /**
         * 加锁,设置key值
         * @param key
         * @param val
         * @return
         */
        public boolean lock(String key,Serializable val){
            //如果不存在该k-v,则设置该k-v并返回true,否则返回false,数值的失效时间设置为2分钟
            if(redisTemplate.opsForValue().setIfAbsent(key,val,2, TimeUnit.MINUTES)){
                return true;
            }
            return false;
        }
    
        /**
         * 删除k-v值,为了避免误删,需要判断值是否一样
         * @param key
         */
        public void unlock(String key,Serializable val){
            long result = redisTemplate.execute(redisScript, Collections.singletonList(key),val);
            System.out.println(result);
        }
    }

    这里的lock方法没有做while重试操作,设置失败后立马返回false;以上工具定义好后可以在系统中的某个方法中使用了:

    @Service
    public class RedisLockUtilTest {
        @Autowired
        private RedisLockUtil redisLock;
        public void test(String xxx){
            String randomVal=UUID.randomUUID().toString();
         //加锁
         redisLock.lock(xxx,randomVal);
         //todo xxx逻辑操作
         //解锁
    redisLock.unLock(xxx,randomVal);
    }
    }

    以上是一个redis分布式锁的简单设计,还有很多需要改进的地方。。。

  • 相关阅读:
    《javascript实战》Part1——2成功javascript开发者的7个习惯
    《javascript实战》Part1——1
    [转载]——技术人员如何去面试?
    [转载]javascript练习(二)JS实现淘宝幻灯片效果
    [转载]——To 注释 or not 注释, that is a question
    [转载]javascript练习(一)JS仿Flash图片切换效果
    [转载]——网站前端优化一些小经验
    w3c盒式模型/ie盒式模型
    jQuery-动画 animate() hide() show() toggle() fadeT0() slideToggle()
    24.OOP面向对象;
  • 原文地址:https://www.cnblogs.com/codeMedita/p/15781081.html
Copyright © 2011-2022 走看看