Redisson Unable to acquire subscription lock 异常
🏷️ Redisson
今天在使用 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 及以上的版本就可以解决这个问题了。