概述
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做分布式锁所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复