Skip to content
欢迎扫码关注公众号

Redisson Unable to acquire subscription lock 异常

今天在使用 Redisson 的 RedissonDelayedQueue 功能时,项目启动报了如下错误:

txt
Caused by: org.redisson.client.RedisTimeoutException: Unable to acquire subscription lock after 2ms. Try to increase 'subscriptionTimeout', 'subscriptionsPerConnection', 'subscriptionConnectionPoolSize' parameters.
    at org.redisson.pubsub.PublishSubscribeService.lambda$timeout$12(PublishSubscribeService.java:328)
    at io.netty.util.HashedWheelTimer$HashedWheelTimeout.run(HashedWheelTimer.java:715)
    at io.netty.util.concurrent.ImmediateExecutor.execute(ImmediateExecutor.java:34)
    at io.netty.util.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:703)
    at io.netty.util.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:790)
    at io.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:503)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:833)

提示去修改 subscriptionTimeout 参数,但是这个参数默认是 7500 ms,完全够用的。

仔细查看了 Redisson 的源码,果然发现了一些奇怪的地方。

java
private CompletableFuture<PubSubConnectionEntry> subscribe(PubSubType type, Codec codec, ChannelName channelName,
                                                            MasterSlaveEntry entry, ClientConnectionsEntry clientEntry, RedisPubSubListener<?>... listeners) {
    CompletableFuture<PubSubConnectionEntry> promise = new CompletableFuture<>();
    AsyncSemaphore lock = getSemaphore(channelName);
    int timeout = config.getSubscriptionTimeout();
    long start = System.nanoTime();
    Timeout lockTimeout = connectionManager.getServiceManager().newTimeout(t -> {
        promise.completeExceptionally(new RedisTimeoutException(
                "Unable to acquire subscription lock after " + timeout + "ms. " +
                        "Try to increase 'timeout', 'subscriptionsPerConnection', 'subscriptionConnectionPoolSize' parameters."));
    }, timeout, TimeUnit.MILLISECONDS);
    lock.acquire().thenAccept(r -> {
        if (!lockTimeout.cancel() || promise.isDone()) {
            lock.release();
            return;
        }

        long newTimeout = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
        subscribeNoTimeout(codec, channelName, entry, clientEntry, promise, type, lock, new AtomicInteger(), listeners);
        timeout(promise, newTimeout);
    });
    return promise;
}

第 18 行计算的超时时间 newTimeout 大概率会非常小,很容易导致超时的发生。去源码仓库看了下这个文件,发现在 3.24.2 版本(当前使用的版本是 3.24.1)及之后,这行代码被修改为了如下形式。

java
long newTimeout = timeout - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

综上,只需要将 Redisson 升级到 3.24.2 及以上的版本就可以解决这个问题了。

Page Layout Max Width

Adjust the exact value of the page width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the page layout
A ranged slider for user to choose and customize their desired width of the maximum width of the page layout can go.

Content Layout Max Width

Adjust the exact value of the document content width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the content layout
A ranged slider for user to choose and customize their desired width of the maximum width of the content layout can go.