我是靠谱客的博主 暴躁小白菜,最近开发中收集的这篇文章主要介绍如何使用redis做分布式锁,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1.在使用redis做分布式锁得前置条件需要满足以下三点:

a.互斥性

b.不能发送死锁

c.解铃还须系铃人

一般采用redis 集群模式,保证缓存服务得高可用,和一定得容错性。

  

         /**
     * 获取锁
     * @param lockKey
     * @param value
     * @param expireTime:单位-秒
     * @return
     */
    public boolean getLock(String lockKey, String value, int expireTime){
        boolean ret = false;
        try{
            String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";
 
            RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
 
            Object result = redisTemplate.execute(redisScript,new StringRedisSerializer(),new StringRedisSerializer(), Collections.singletonList(lockKey),value,expireTime + "");
            System.out.println(result + "-----------");
            //Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value,expireTime + "");
 
            if(SUCCESS.equals(result)){
                return true;
            }
 
        }catch(Exception e){
            e.printStackTrace();
        }
        return ret;
    }

 
   

/**
     * 释放锁
     * @param lockKey
     * @param value
     * @return
     */
    public boolean releaseLock(String lockKey, String value){
 
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
 
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
 
        Object result = redisTemplate.execute(redisScript,new StringRedisSerializer(),new StringRedisSerializer(), Collections.singletonList(lockKey),value);
        if(SUCCESS.equals(result)) {
            return true;
        }
 
        return false;
    }


   以上代码已经在我的项目中确切可以使用了。但是在使用的过程中遇到了许多问题。

   1:java.lang.IllegalStateException

   在返回值方面,会经常报IllegalStateException。

RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
   用String类型时候,经常会报类型转换异常。我在代码中使用的Long类型接收该类型,在命令行中我们也看到命令行结果返回的是数字0或者1,保险起见我们也可以用Object对象来接收结果集。

 2:ERR value is not an integer or out of range

  这个问题纠结了我一个下午至少,Redis报的异常都是很深的,从跟踪源码的时候看到,我们在调用redisTemplate.execute的方法时候,如果不传序列化的参数的时候,代码默认调用的是 Jdkserializationredisserializer 来进行序列化和反序列化操作,这是jdk自带的序列化操作,使用该序列化的对象必须要实现Serializable接口。所以该序列化接口是用于对实体类的序列化。

   所以在进行 execute 操作的时候,我们传入 Stringredisserializer,该序列化接口是专用于对字符串类型的序列化操作。具体的区别可以去这两个类的源码中看下他们的加密方式。
 

 

第二种方式jedis的情况下话不多说上代码,取锁代码:

public class RedisLockPool{

private static final String IS_SUCCESS = "OK";
private static final String IF_EXIST = "NX";
private static final String TIME_OUT = "PX";

public static boolean getLock(Jedis jedis, String lockKey, String lockValue, Long timeOut){
        //1.设置一个redis 的 key(固定的)
        //2.value每次设置都不一样(客户端上锁标识,意味解锁必须是上锁之人)
        //3.IF_EXIST 插入数据前会判断是否存在,如果存在则插入会不成功,说明锁已经有人使用了
        //4.TIME_OUT 设置锁的超时时间,根据第四个参数而定
        //5.争取一条指令实现取锁动作,保持原子性
        String result = jedis.set(lockKey, lockValue, IF_EXIST, TIME_OUT, timeOut);
        if(IS_SUCCESS.equels(result)){
            //成功取到锁
            return true;            
        }
        return false;
}

}

话不多说继续释放锁代码:

private static final Long IS_SUCCESS = 1L;

public static void redisReleaseLock(Jedis jedis, String lockKey, String lockValue){
    //lua脚本: 先取得key 的值,
    //然后取判断其value和传入的value是否相等,
    //如果是相等则删除该KEY
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    //为了保证原子性,
    //因为redis的特性为认为eval是一条命令,
    //它只会执行完命令后才会去执行其他的指令
    Long result = jedis.eval(script,   Collections.singletonList(lockKey),Collections.singletonList(lockValue))
    if(IS_SUCCESS.equels(result)){
        return true;
    }
    return false;
}

 

最后

以上就是暴躁小白菜为你收集整理的如何使用redis做分布式锁的全部内容,希望文章能够帮你解决如何使用redis做分布式锁所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(83)

评论列表共有 0 条评论

立即
投稿
返回
顶部