You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redisson调用lock.unlock()抛出RedisException异常,寻求解决办法

问题:Redisson分布式锁解锁时出现Redis发布权限异常

我在分布式锁服务中使用以下通用代码实现锁逻辑:

private <T> T lockAndProcessTask(String lockKey, boolean waitForLockRelease, Supplier<T> task, Supplier<T> onLockAcquireFail) {
    boolean isLockAcquired = false;
    RLock lock = null;
    T result;
    try {
        val lockAcquireWaitTime = waitForLockRelease ? LOCK_ACQUIRE_WAIT_TIME_MS : 0;
        try {
            lock = redissonClient.getLock(lockKey);
            isLockAcquired = lock.tryLock(lockAcquireWaitTime, LOCK_LEASE_TIME_MS, MILLISECONDS);
        } catch (Exception e) {
            log.error("Failed to lock, lockKey = {}", lockKey, e);
        }
        if (isLockAcquired) {
            log.debug("Lock acquired, lockKey = {}", lockKey);
            result = task.get();
        } else {
            log.debug("Failed to acquire lock, lockKey = {}", lockKey);
            result = onLockAcquireFail.get();
        }
    } finally {
        try {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        } catch (Exception e) {
            log.error("Failed to unlock, lockKey = {}", lockKey, e);
        }
    }
    return result;
}

调用RLock.unlock()时出现如下异常:

2023-05-11 21:42:22 [cs-async-358] ERROR c.w.c.s.s.DistributedLockerService - Failed to unlock, lockKey = TEST_LOCK_9000
org.redisson.client.RedisException: Unexpected exception while processing command
Caused by: java.util.concurrent.CompletionException: org.redisson.client.RedisException: ERR The user executing the script can't publish to the channel mentioned in the command script: 4dfe249c1asdvdff2dbd408626b9aff7, on @user_script:1.. channel: [id: 0xe6b7d174, L:/172.20.0.5:45526 - R:121.187.117.226/156.121.137.203:6379] command: (EVAL), promise: java.util.concurrent.CompletableFuture@22484e24[Not completed], params: [if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then return nil;end; local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); if (counter > 0) then redis.call('pexpire', KEYS[1], ARGV[2]); return 0; else redis.call('del', KEYS[1]); redis.call('publish', KEYS[2], ARGV[1]); return 1; end; return nil;, 2, TEST_LOCK_9000, redisson_lock__channel:{TEST_LOCK_9000}, 0, 3600000, 4ac2378c-7582-4f9a-9cdfa7-8a6823e4709a:57312]
        at org.redisson.RedissonBaseLock.lambda$evalWriteAsync$0(RedissonBaseLock.java:228)
        at java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:822)
        at java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:797)
        at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
        at java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:1977)
        at org.redisson.command.CommandBatchService.lambda$executeAsync$7(CommandBatchService.java:284)
        at java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:760)
        at java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:736)
        at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)

使用的Redisson依赖版本:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.19.3</version>
</dependency>

解决思路

1. 检查Redis用户权限配置

异常核心信息是The user executing the script can't publish to the channel,说明当前Redis账号缺少PUBLISH权限,或者对Redisson锁通知频道(如redisson_lock__channel:{TEST_LOCK_9000})无发布权限:

  • 登录Redis服务器,执行ACL GETUSER <你的用户名>查看当前用户权限列表,确认是否包含PUBLISH;
  • 若权限缺失,执行ACL SETUSER <你的用户名> ON +PUBLISH添加权限,测试环境可临时用ACL SETUSER <你的用户名> ON ~* +@all赋予全权限(生产环境谨慎操作);
  • 若使用Redis 6.x以下版本,检查redis.conf是否通过rename-command禁用了PUBLISH命令,需恢复该命令。

2. 切换Redisson锁类型规避发布操作

如果无法修改Redis权限,可改用不依赖发布订阅的锁类型:

  • 使用RedissonSpinLock,它基于自旋实现锁逻辑,不会触发Redis的PUBLISH命令;
  • 修改锁获取代码:lock = redissonClient.getSpinLock(lockKey);,注意该锁无自动续期功能,需确保任务执行时间短于锁租期。

3. 升级Redisson版本

当前使用的3.19.3版本可能存在权限兼容问题,检查Redisson更新日志后,尝试升级到最新稳定版(如3.23.x),新版本可能优化了锁释放时的权限处理逻辑。

4. 验证锁释放逻辑合理性

虽然现有代码已做lock.isHeldByCurrentThread()判断,仍需确认:

  • 是否存在重复解锁场景,比如任务执行过程中其他代码也调用了解锁;
  • 锁租期是否合理,若任务执行超时导致锁自动释放,后续调用unlock也会触发异常(此为次要检查点,当前异常核心是权限问题)。

内容的提问来源于stack exchange,提问作者vot_vam_i_xixi_xaxa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 22:35:02