Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。
分布式锁
分布式锁是我们工作中最常用的功能,让我们看看它是如何实现的。
RLock rlock = redisson.getLock(lockKey);
boolean isLocked = rlock.tryLock(0, 15, TimeUnit.SECONDS);
rlock.unlock();
首先,我们主要去关注加锁的过程,那么对于redisson创建,通信等一些涉及底层的代码就只能忽略了,将注意力放到核心的流程代码上。对于getLock(String name)方法,
public RLock getLock(String name) {
return new RedissonLock(this.connectionManager.getCommandExecutor(), name);
}
这里的 RLock 是继承自 java.util.concurrent.locks.Lock 的一个 interface,getLock 返回的实际上是其实现类 RedissonLock 的实例。
来看看构造 RedissonLock 的参数
- commandExecutor: 与 Redis 节点通信并发送指令的真正实现。需要说明一下,Redisson 缺省的 CommandExecutor 实现是通过 eval 命令来执行 Lua 脚本,所以要求 Redis 的版本必须为 2.6 或以上,否则你可能要自己来实现CommandExecutor。
- name: 锁的全局名称,例如上面代码中的 “foobar”,具体业务中通常可能使用共享资源的唯一标识作为该名称。
- id: Redisson 客户端唯一标识,实际上就是一个 UUID.randomUUID()。
返回的是一个RedissonLock
public RedissonLock(CommandAsyncExecutor commandExecutor, String name) {
super(commandExecutor, name);
this.commandExecutor = commandExecutor;
this.id = commandExecutor.getConnectionManager().getId();
this.internalLockLeaseTime = commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout();
}
- 传入了一个异步处理的命令执行器,还有待上锁的name,构造函数中的this.id跟进去看,其实就是一个UUID对象,是当前要加锁客户端的唯一标识;
- 这里还有一个internalLockLeaseTime,从字面上来看,是一个内部的锁续约时间,默认是30000毫秒。
- this.entryName将UUID和传入的锁名称拼接起来,猜测一下,这个可能就是最终在Redis中要存储的加锁key。
下面进入加锁逻辑,核心代码很简单,就是tryLock,他的运行逻辑是怎么样的呢?
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
long time = unit.toMillis(waitTime);
long current = System.currentTimeMillis();
final long threadId = Thread.currentThread().getId();
//申请锁返回剩余锁的过期时间
Long ttl = this.tryAcquire(leaseTime, unit, threadId);
//如果空表示申请锁成功
if (ttl == null) {
return true;
} else {
time -= System.currentTimeMillis() - current;
if (time <= 0L) {
this.acquireFailed(threadId);
return false;
} else {
current = System.currentTimeMillis();
//订阅监听redis消息,并且创建RedissonLockEntry,其中RedissonLockEntry中比较关键的是一个 Semaphore属性对象,用来控制本地的锁请求的信号量同步,返回的是netty框架的Future实现。
final RFuture<RedissonLockEntry> subscribeFuture = this.subscribe(threadId);
//阻塞等待subscribe的future的结果对象,如果subscribe方法调用超过了time,说明已经超过了客户端设置的最大wait time,则直接返回false,取消订阅,不再继续申请锁了。
if (!this.await(subscribeFuture, time, TimeUnit.MILLISECONDS)) {
if (!subscribeFuture.cancel(false)) {
subscribeFuture.addListener(new FutureListener<RedissonLockEntry>() {
public void operationComplete(Future<RedissonLockEntry> future) throws Exception {
if (subscribeFuture.isSuccess()) {
RedissonLock.this.unsubscribe(subscribeF