redis测试项目搭建
新建项目
http://start.spring.io/填写项目标识、添加redis依赖,下载项目用idea打开
配置redis
- 启动本地redis服务器
- 在application.properties文件添加配置
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接超时时间(毫秒)
spring.redis.timeout=30000
- 测试redis是否可用
public class RedisConnectTest extends RedisApplicationTests{
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void testRedis() throws Exception {
stringRedisTemplate.opsForValue().set("a", String.valueOf(1));
System.out.println(stringRedisTemplate.opsForValue().get("a"));
}
}
场景
锁
定义锁的工具类接口
public interface LockHelper {
void lock(String key);
void unLock(String key);
}
调用的时候注意在finally的时候释放锁,避免业务执行失败时没释放所导致死锁
try {
lockHelper.lock(account.id);
super.run();
}finally {
lockHelper.unLock(account.id);
}
本地锁
ConcurrentHashMap+ReentrantLock:
@Component("LocalLockHelper")
public class LocalLockHelper implements LockHelper {
private ConcurrentHashMap<String,Lock> lockMap = new ConcurrentHashMap<>();
@Override
public void lock(String key) {
Lock lock = lockMap.get(key);
if(lock == null){
Lock newLock = new ReentrantLock();
Lock existLock = lockMap.putIfAbsent(key,newLock);
//锁不存在的话就用新锁,存在的话用旧锁
lock = existLock == null?newLock:existLock;
}
lock.lock();
}
@Override
public void unLock(String key) {
Lock lock = lockMap.get(key);
if(lock != null){
lock.unlock();
}
}
}
redis锁
加锁多了个支持锁过期的方法,防止分布式情况下一些客户端没有(成功)释放锁,甚至在第一次申请锁的时候对应的key已经存在。
@Component("RedisLockHelper")
public class RedisLockHelper implements LockHelper,ExpireLock{
@Autowired
private StringRedisTemplate redisTemplate;
private Map<String, String> keyMap = new HashMap<>();
@Override
public void lock(String key) {
BoundValueOperations<String,String> boundValueOperations =
this.redisTemplate.boundValueOps(getLockKey(key));
while (true) {
if (boundValueOperations.setIfAbsent(this.getRequestId())) {
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
*设置持有锁的超时时间,防止某些客户端未释放锁
*/
@Override
public void lock(String key, long expireTimeMills) {
BoundValueOperations<String,String> boundValueOperations =
this.redisTemplate.boundValueOps(getLockKey(key));
while (true) {
if (boundValueOperations.setIfAbsent(this.getRequestId())) {
boundValueOperations.expire(expireTimeMills, TimeUnit.MILLISECONDS);
break;
}else {
long wait;
if (boundValueOperations.getExpire() == -1) {
System.out.println(key+"|"+Thread.currentThread().getName()+"设置锁超时时长");
//防止未释放锁
wait = expireTimeMills;
boundValueOperations.expire(expireTimeMills, TimeUnit.MILLISECONDS);
}else{
wait = 100;
}
try {
Thread.sleep(wait);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
@Override
public void unLock(String key) {
String lk = getLockKey(key);
BoundValueOperations<String,String> boundValueOperations =
this.redisTemplate.boundValueOps(lk);
if(this.getRequestId().equals(boundValueOperations.get())) {
// 释放锁的时候检测当前值是否为请求id,防止被其他请求释放,这个地方应该用lua脚本 把检查和删除 做成一个有原子性的方法或者命令
redisTemplate.delete(lk);
}
}
private String getRequestId() {
//多实例情况下需要设置为http requestId之类的
return String.valueOf(Thread.currentThread().getId());
}
private String getLockKey(String key) {
String t = keyMap.get(key);
if(t == null){
t = "lock:"+key;
keyMap.put(key,t);
}
return t;
}
}
网友评论