本文为博主原创,未经允许不得转载:
1. Jedis 实现分布式锁
2. Redission 实现分布式锁
为了确保分布式锁可用,至少要保证锁的实现同时满足以下几个条件
1.1 引入 jedis 依赖:
redis.clients
jedis
2.9.0
1.2 Jedis 封装工具类,封装分布式锁及解锁方法
package com.example.demo.util;
import redis.clients.jedis.Jedis;
import java.util.Collections;
public class JedisUtils {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static final Long RELEASE_SUCCESS = 1L;
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
* 释放分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
获取分布式锁使用的是 Jedis 内部封装的 set(String key, String value, String nxxx, String expx, int time) 方法。该方法可保证 redis 获取分布式锁操作的原子性。在网上会看到通过 jedis 的 setnx (lockKey, requestId) 与 jedis.expire(lockKey, expireTime) 两步来获取分布式锁,这种将获取分布式锁 分为两步或三步的,并添加逻辑校验的,往往看起来并没什么问题,但这种破坏了原子性,在高并发场景性,会存在丢锁的场景。建议使用以上的方式 获取分布式锁。同理分布式锁解锁也一样,而 lua 脚本具有天然的原子性,可以保证执行的安全性。
redisson简单易用、支持锁重入、支持阻塞等待、Lua脚本原子操作等,内部封装了很多redis 的解决方案,使用reids 客户端时,优先推荐使用 redission 客户端。
2.1 引入依赖:
org.redisson
redisson
3.8.2
2.2 定义 redisision 客户端配置
package com.example.demo.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient getClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
return redisson;
}
}
2.3 常用方法:
package com.example.demo.util;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RedissionUtils {
private RedissonClient redissonClient;
public void test(){
RLock lock = redissonClient.getLock("lockName");
try{
// 1. 最常见的使用方法
lock.lock();
boolean lockResult = lock.tryLock();
// 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
//lock.lock(10, TimeUnit.SECONDS);
// 3. 尝试加锁,最多等待2秒,上锁以后8秒自动解锁
boolean res = lock.tryLock(2, 8, TimeUnit.SECONDS);
if(res){ //成功
//处理业务
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
}
方法说明:
lock.lock(); 与 boolean lockResult = lock.tryLock(); 方法均为获取分布式锁,前面方法无返回值,后面方法返回值为 boolean 类型。该方法获取分布式锁会自动续锁,即通过redission 内部封装的看门狗进行任务续时,jedis 分布式锁不支持任务续时,如果在锁时间内,任务尚未执行完,则会丢锁。
lock.lock(); 与 lock.tryLock(); 方法如果带有入参,则不会执行看门狗模式,即不会分布式锁续时。
boolean tryLock(long waitTime, long leaseTime, TimeUnit var5) throws InterruptedException; 获取分布式最多等待时间waitTime,分布式锁过期时间leaseTime。 续时任务执行的源码中可看到看门狗模式的执行,代码如下:
private RFuture tryAcquireOnceAsync(long leaseTime, TimeUnit unit, final long threadId) {
if (leaseTime != -1L) {
// 传参
return this.tryLockInnerAsync(leaseTime, unit, threadId,
RedisCommands.EVAL_NULL_BOOLEAN);
} else {
// 不传参。 getLockWatchdogTimeout ,看门狗监听超时,如果超时则续时
RFuture ttlRemainingFuture = this.tryLockInnerAsync(this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN); ttlRemainingFuture.addListener(new FutureListener() {
public void operationComplete(Future future) throws Exception {
if (future.isSuccess()) {
Boolean ttlRemaining = (Boolean)future.getNow();
if (ttlRemaining) {
RedissonLock.this.scheduleExpirationRenewal(threadId);
}
}
}
}
);
return ttlRemainingFuture;
}
}
lock.unlock(); 释放分布式锁。
3. redis 分布式锁优化思考: https://mp.weixin.qq.com/s/RLeujAj5rwZGNYMD0uLbrg