Redisson——RFuture设计

Redisson——RFuture设计

1. 分享目的

之前在分析Redisson分布式加锁解锁过程中,RFuture几乎贯彻了整个源码,从类名可以联想到Java的Future。从而很自然的觉得跟异步多线程有关,从而产生了好奇。之前由于需要对分布式加锁解锁有个整体的认识,没有仔细分析RFuture,当回过头分析RFuture的时候才发现之前的做法是对的,因为RFuture有点难懂,这里通过解锁代码分析其原理。

RFuture的设计目的主要是为了异步多线程,借助了CompletableFutureNettyFuture/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);
    });
}

通过调用NettyPromise实现,添加监听器,来看看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还是比较难理解的,但结合CompletableFutureNetty的Future\Promise机制可以对整体的运行逻辑有个大概的理解,RFuture的设计总的来说是为了异步多线程,结合CompletableFuture的使用,对redis的通讯进行封装。



世界那么大,感谢遇见,未来可期…

欢迎同频共振的那一部分人

Tarzan写bug

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值