Redisson——RFuture设计
1. 分享目的
之前在分析Redisson
分布式加锁解锁过程中,RFuture
几乎贯彻了整个源码,从类名可以联想到Java的Future
。从而很自然的觉得跟异步多线程有关,从而产生了好奇。之前由于需要对分布式加锁解锁有个整体的认识,没有仔细分析RFuture
,当回过头分析RFuture
的时候才发现之前的做法是对的,因为RFuture
有点难懂,这里通过解锁代码分析其原理。
RFuture
的设计目的主要是为了异步多线程,借助了CompletableFuture
和Netty
的Future/Promise
机制。
2. 源码分析
在追踪解锁源码RedissonLock.unlock()
中,会调用到unlockAsync()
@Override
public RFuture<Void> unlockAsync(long threadId) {
RPromise<Void> result = new RedissonPromise<Void>();
// 删除redis中的锁,并发送解锁消息
RFuture<Boolean> future = unlockInnerAsync(threadId);
future.onComplete((opStatus, e) -> {
// 取消watchdog
cancelExpirationRenewal(threadId);
if (e != null) {
result.tryFailure(e);
return;
}
if (opStatus == null) {
IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
+ id + " thread-id: " + threadId);
result.tryFailure(cause);
return;
}
result.trySuccess(null);
});
return result;
}
这是RFuture的一个典型用法结构,unlockInnerAsync()
中异步发送命令给redis进行执行,当异步执行成功后执行onComplete()
中定义的逻辑。
这里会有两个疑问:
1. redis命令是怎么被异步执行的
发送命令给redis是在CommandAsyncService.evalAsync()
,其实加锁的redis命令也是通过这个方法执行
CommandAsyncService
private <T, R> RFuture<R> evalAsync(NodeSource nodeSource, boolean readOnlyMode, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object... params) {
......
......
RPromise<R> mainPromise = createPromise();
List<Object> args = new ArrayList<Object>(2 + keys.size() + params.length);
args.add(script);
args.add(keys.size());
args.addAll(keys);
args.addAll(Arrays.asList(params));
async(readOnlyMode, nodeSource, codec, evalCommandType, args.toArray(), mainPromise, false);
return mainPromise;
}
public <V, R> void async(boolean readOnlyMode, NodeSource source, Codec codec,
RedisCommand<V> command, Object[] params, RPromise<R> mainPromise,
boolean ignoreRedirect) {
RedisExecutor<V, R> executor = new RedisExecutor<>(readOnlyMode, source, codec, command, params, mainPromise, ignoreRedirect, connectionManager, objectBuilder);
executor.execute();
}
redis命令会放到RedisExecutor.execute()
中执行,而最终执行redis命令的是getConnection()
protected RFuture<RedisConnection> getConnection() {
RFuture<RedisConnection> connectionFuture;
if (readOnlyMode) {
connectionFuture = connectionManager.connectionReadOp(source, command);
} else {
connectionFuture = connectionManager.connectionWriteOp(source, command);
}
return connectionFuture;
}
最终会到ConnectionPool.acquireConnection()
中异步执行。
2. onComplete()是怎样监听到异步执行成功的?onComplete()在异步任务执行成功后才定义怎么办?
来看看RedissonPromise.onComplete()
实现
private final Promise<T> promise = ImmediateEventExecutor.INSTANCE.newPromise();
@Override
public void onComplete(BiConsumer<? super T, ? super Throwable> action) {
promise.addListener(f -> {
if (!f.isSuccess()) {
action.accept(null, f.cause());
return;
}
action.accept((T) f.getNow(), null);
});
}
通过调用Netty
的Promise
实现,添加监听器,来看看addListener()
的默认实现DefaultPromise.addListener()
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
ObjectUtil.checkNotNull(listener, "listener");
synchronized(this) {
this.addListener0(listener);
}
if (this.isDone()) {
this.notifyListeners();
}
return this;
}
当添加监听器时,如果异步任务已完成会立即执行监听器,否则等待异步任务完成后会调用notifyListeners()
执行监听器。
3. CommandAsyncService.get()
返回解锁源码
@Override
public void unlock() {
try {
get(unlockAsync(Thread.currentThread().getId()));
} catch (RedisException e) {
if (e.getCause() instanceof IllegalMonitorStateException) {
throw (IllegalMonitorStateException) e.getCause();
} else {
throw e;
}
}
}
当定义好异步线程RFuture
后,会到get()
中,而其中最终调用CommandAsyncService.get()
,等待返回结果
@Override
public <V> V get(RFuture<V> future) {
try {
future.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
if (future.isSuccess()) {
return future.getNow();
}
throw convertException(future);
}
而future.await()
也是调用了Netty的实现DefaultPromise.await()
,当异步任务成功时则直接返回,否则调用JDK的wait()
阻塞等待异步任务执行完。
DefaultPromise.await()
public Promise<V> await() throws InterruptedException {
if (this.isDone()) {
return this;
} else if (Thread.interrupted()) {
throw new InterruptedException(this.toString());
} else {
this.checkDeadLock();
synchronized(this) {
while(!this.isDone()) {
this.incWaiters();
try {
this.wait();
} finally {
this.decWaiters();
}
}
return this;
}
}
}
3. 总结
RFuture
还是比较难理解的,但结合CompletableFuture
和Netty的Future\Promise
机制可以对整体的运行逻辑有个大概的理解,RFuture
的设计总的来说是为了异步多线程,结合CompletableFuture的使用,对redis的通讯进行封装。
世界那么大,感谢遇见,未来可期…
欢迎同频共振的那一部分人
Tarzan写bug