1. Redisson 简介
Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格 (In-Memory Data Grid)。它不仅提供一系列的分布式的 Java 常用对象,还提供了许多分布式服务。Redisson 提供了使用 Redis 的最简单的和最便捷的方法。Redisson 的宗旨是促进使用者对 Redis 的关注分离 (Separation of Concern),从而让使用者能够将精力更集中地放到处理业务逻辑上。
为了防止用户重复提交请求,我们需要对相同参数的请求增加分布式锁。因此考虑使用 Redisson 来实现此功能。
2. 如何使用 Redisson?
1) pom 中加入依赖包
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
</dependency>
2) 配置
Redisson 支持单节点、集群、哨兵等模式的配置。此处,我们使用单节点的配置方式。
@Bean
public RedissonClient getRedisson() throws Exception {
Config config = new Config();
String redisAddress = "redis://" + host + ":" + port;
config.useSingleServer()
.setAddress(redisAddress).setPassword(password);
return Redisson.create(config);
}
3) 分布式锁
接口:定义两个方法。
tryLock
方法,尝试获取锁,直到 waitTime
,如果获取到锁,返回 true;否则,返回 false。等到 leaseTime
, 此锁会自动释放。
unlock
方法,尝试释放锁。释放锁的时候会检查当前线程是否持有这个锁 (isHeldByCurrentThread()
),如果没有持有锁,则抛出异常。
public interface DistributedLocker {
/**
* 获取锁
* @param lockKey 分布式锁的key
* @param unit 时间单位
* @param waitTime 等待时间
* @param leaseTime 自动释放时间
* @return 是否获取到锁
*/
boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);
void unlock(String lockKey);
}
实现类
@Component
public class DistributedLockerImpl implements DistributedLocker {
@Autowired
private RedissonClient redissonClient;
@Override
public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
try {
RLock lock = redissonClient.getFairLock(lockKey);
;
return lock.tryLock(waitTime, leaseTime, unit);
} catch (Exception e) {
log.error("tryLock error, lockKey: {}, error: ", lockKey, e);
return true;
}
}
@Override
public void unlock(String lockKey) {
try {
RLock lock = redissonClient.getFairLock(lockKey);
lock.unlock();
} catch (Exception e) {
log.error("unlock error, lockKey: {}, error: ", lockKey, e);
}
}
}
4)使用分布式锁
String key = "test_key#123";
// 在5s内,如果获取到锁,返回 true,否则返回 false;
// 在10s后,主动释放锁
boolean isGetLock = distributedLocker.tryLock(key, TimeUnit.SECONDS, 5, 10);
if (isGetLock) {
// 具体的业务逻辑
...
// 只在获取到锁的情况下,才需要释放锁
distributedLocker.unlock(key);
} else {
// 具体的业务逻辑
...
}
3. 使用过程中遇到的问题
1) 只处理了单一接口的情况,未处理组合接口的情况,导致组合接口请求都被锁住等待 (因为没有主动释放锁,只有等到 releaseTime );
总结:对功能修改的评估需要全面,防止出现遗漏的情况
2) 第一次上线之后出现重复释放锁的情况
总结:尽量减少不必要的操作。尤其是对于分布式锁的操作,需要保证加锁-释放锁的操作成对出现,避免出现重复释放锁导致的开销以及锁操作异常问题。
3)第一次上线之后出现:某线程解锁之后,其他线程继续等待直到超时的情况。
原因分析:分布式锁所依赖的组件 Redisson 在阿里云集群 Redis 环境下会出现这种情况 github issue
解决方案:修改到单机版的 Redis,解决此问题。
总结: 1) 引入新的技术组件,需要经过充分的评估和测试。包括其对于环境的依赖的测试。
2) 尽量保持线上环境和开发测试环境的一致,包括redis、mysql等的版本等。使得能在测试环节可以提前发现问题。




