110期面试官:Redis分布式锁如何解决锁超时问题?
Posted 程序员的成长之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了110期面试官:Redis分布式锁如何解决锁超时问题?相关的知识,希望对你有一定的参考价值。
阅读本文大概需要 6.5 分钟。
来自:www.jianshu.com/p/39b3570d3b56
一、前言
二、准备工作
https://pan.baidu.com/share/init?surl=NN0c0tDYQjBTTPA-WTT3yg
提取码: 8f2a
https://pan.baidu.com/share/init?surl=NoJtZZZOXsk45aQYtveWbQ
提取码: 9bhf
https://pan.baidu.com/share/init?surl=28sGJk4zxoOknAd-47hE7w
提取码: vfu7
三、说明
<!--redis-->
<dependency>
<groupId>org.springframework.data </groupId>
<artifactId>spring-data-redis </artifactId>
<version>1.6.5.RELEASE </version>
</dependency>
<dependency>
<groupId>redis.clients </groupId>
<artifactId>jedis </artifactId>
<version>2.7.3 </version>
</dependency>
分布式锁工具类: DistributedLock
测试接口类: PcInformationServiceImpl
锁延时守护线程类: PostponeTask
四、实现思路
五、实现
1、版本01代码
1)、DistributedLock
package com.cn.pinliang.common.util;
import com.cn.pinliang.common.thread.PostponeTask;
import com.google.common.collect.Lists;
import org.springframework.beans.factory. annotation.Autowired;
import org.springframework. data.redis.core.RedisCallback;
import org.springframework. data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.io.Serializable;
import java.util.Collections;
@Component
public class DistributedLock {
@Autowired
private RedisTemplate<Serializable, Object> redisTemplate;
private static final Long RELEASE_SUCCESS = 1L;
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "EX";
// 解锁脚本(lua)
private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
/**
* 分布式锁
* @param key
* @param value
* @param expireTime 单位: 秒
* @return
*/
public boolean lock(String key, String value, long expireTime) {
return redisTemplate.execute((RedisCallback< Boolean>) redisConnection -> {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
String result = jedis. set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
});
}
/**
* 解锁
* @param key
* @param value
* @return
*/
public Boolean unLock(String key, String value) {
return redisTemplate.execute((RedisCallback< Boolean>) redisConnection -> {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
Object result = jedis.eval(RELEASE_LOCK_SCRIPT, Collections.singletonList(key), Collections.singletonList(value));
if (RELEASE_SUCCESS.equals(result)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
});
}
}
2)、PcInformationServiceImpl
public JsonResult add() throws Exception {
String key = "add_information_lock";
String value = RandomUtil.produceStringAndNumber( 10);
long expireTime = 10L;
boolean lock = distributedLock. lock(key, value, expireTime);
String threadName = Thread.currentThread().getName();
if ( lock) {
System. out.println(threadName + " 获得锁...............................");
Thread.sleep( 30000);
distributedLock.unLock(key, value);
System. out.println(threadName + " 解锁了...............................");
} else {
System. out.println(threadName + " 未获取到锁...............................");
return JsonResult.fail( "未获取到锁");
}
return JsonResult.succeed();
}
3)、打开redis-desktop-manager客户端, 刷新缓存, 可以看到, 此时是没有add_information_lock的key的
4)、启动jmeter, 调用接口测试
2、版本02代码
1)、DistributedLock
package com.cn.pinliang.common.util;
import com.cn.pinliang.common.thread.PostponeTask;
import com.google.common.collect.Lists;
import org.springframework.beans.factory. annotation.Autowired;
import org.springframework. data.redis.core.RedisCallback;
import org.springframework. data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.io.Serializable;
import java.util.Collections;
@Component
public class DistributedLock {
@Autowired
private RedisTemplate<Serializable, Object> redisTemplate;
private static final Long RELEASE_SUCCESS = 1L;
private static final Long POSTPONE_SUCCESS = 1L;
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "EX";
// 解锁脚本(lua)
private static 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 static final String POSTPONE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('expire', KEYS[1], ARGV[2]) else return '0' end";
/**
* 分布式锁
* @param key
* @param value
* @param expireTime 单位: 秒
* @return
*/
public boolean lock(String key, String value, long expireTime) {
// 加锁
Boolean locked = redisTemplate.execute((RedisCallback< Boolean>) redisConnection -> {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
String result = jedis. set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
});
if (locked) {
// 加锁成功, 启动一个延时线程, 防止业务逻辑未执行完毕就因锁超时而使锁释放
PostponeTask postponeTask = new PostponeTask(key, value, expireTime, this);
Thread thread = new Thread(postponeTask);
thread.setDaemon( Boolean.TRUE);
thread.start();
}
return locked;
}
/**
* 解锁
* @param key
* @param value
* @return
*/
public Boolean unLock(String key, String value) {
return redisTemplate.execute((RedisCallback< Boolean>) redisConnection -> {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
Object result = jedis.eval(RELEASE_LOCK_SCRIPT, Collections.singletonList(key), Collections.singletonList(value));
if (RELEASE_SUCCESS.equals(result)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
});
}
/**
* 锁延时
* @param key
* @param value
* @param expireTime
* @return
*/
public Boolean postpone(String key, String value, long expireTime) {
return redisTemplate.execute((RedisCallback< Boolean>) redisConnection -> {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
Object result = jedis.eval(POSTPONE_LOCK_SCRIPT, Lists.newArrayList(key), Lists.newArrayList(value, String.valueOf(expireTime)));
if (POSTPONE_SUCCESS.equals(result)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
});
}
}
2)、PcInformationServiceImpl不需要改动
3)、PostponeTask
package com.cn.pinliang.common.thread;
import com.cn.pinliang.common.util.DistributedLock;
public class PostponeTask implements Runnable {
private String key;
private String value;
private long expireTime;
private boolean isRunning;
private DistributedLock distributedLock;
public PostponeTask() {
}
public PostponeTask(String key, String value, long expireTime, DistributedLock distributedLock) {
this.key = key;
this. value = value;
this.expireTime = expireTime;
this.isRunning = Boolean.TRUE;
this.distributedLock = distributedLock;
}
@ Override
public void run() {
long waitTime = expireTime * 1000 * 2 / 3; // 线程等待多长时间后执行
while (isRunning) {
try {
Thread.sleep(waitTime);
if (distributedLock.postpone(key, value, expireTime)) {
System. out.println( "延时成功...........................................................");
} else {
this.stop();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void stop() {
this.isRunning = Boolean.FALSE;
}
}
推荐阅读:
朕已阅
以上是关于110期面试官:Redis分布式锁如何解决锁超时问题?的主要内容,如果未能解决你的问题,请参考以下文章
阿里JAVA面试题剖析:一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?
阿里JAVA面试题剖析:一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?