Redisson分布式锁封装-高级封装

Redisson工具介绍

  1. 锁是保护资源的一种实现方式,事务也是受保护的资源,所以必须是锁包裹事务,事务必须要在锁内执行
  2. 此工具类方便快捷的使用redis分布式锁,提高开发效率
  3. 完整代码封装如下:不废话直接上代码
//RedissonLockException
public class RedissonLockException extends RuntimeException {

    public RedissonLockException(String message) {
        super(message);
    }

    public RedissonLockException(String message, Throwable cause) {
        super(message, cause);
    }

    public RedissonLockException(Throwable cause) {
        super(cause);
    }

    public RedissonLockException() {
        super();
    }
}
//RedissonLockWorker
public interface RedissonLockWorker<T> {

    /**
     * 获取锁成功后执行业务逻辑
     *
     * @return 业务逻辑对象
     * @throws Exception 异常信息
     */
    T execute() throws Exception;
}
//RedissonLockService
public interface RedissonLockService {

    boolean lock(String lockName) throws Exception;

    <T> T lock(String lockName, RedissonLockWorker<T> worker) throws Exception;

    <T> T lock(String lockName, int lockTime, RedissonLockWorker<T> worker) throws Exception;

    <T> T multiLock(List<String> lockNames, RedissonLockWorker<T> worker) throws Exception;

    <T> T multiLock(List<String> lockNames, int lockTime, RedissonLockWorker<T> worker) throws Exception;

    void lockVoid(String lockName, RedissonLockWorker<Void> worker) throws Exception;

    void lockVoid(String lockName, int lockTime, RedissonLockWorker<Void> worker) throws Exception;

    void multiLockVoid(List<String> lockNames, RedissonLockWorker<Void> worker) throws Exception;

    void multiLockVoid(List<String> lockNames, int lockTime, RedissonLockWorker<Void> worker) throws Exception;

    void unLock(String lockName);


    //以下是携带事务的锁
    <T> T lockTransaction(String lockName, RedissonLockWorker<T> worker) throws Exception;

    <T> T lockTransaction(String lockName, int lockTime, RedissonLockWorker<T> worker) throws Exception;

    <T> T multiLockTransaction(List<String> lockNames, RedissonLockWorker<T> worker) throws Exception;

    <T> T multiLockTransaction(List<String> lockNames, int lockTime, RedissonLockWorker<T> worker) throws Exception;


    void lockTransactionVoid(String lockName, RedissonLockWorker<Void> worker) throws Exception;

    void lockTransactionVoid(String lockName, int lockTime, RedissonLockWorker<Void> worker) throws Exception;

    void multiLockTransactionVoid(List<String> lockNames, RedissonLockWorker<Void> worker) throws Exception;

    void multiLockTransactionVoid(List<String> lockNames, int lockTime, RedissonLockWorker<Void> worker) throws Exception;
//RedissonLocker
@Component
@Slf4j
@RequiredArgsConstructor
public class RedissonLocker implements RedissonLockService {

    private static final int LOCK_EXPIRE = 30;
    private static final int MAX_WAIT_TIME = 10;
    private final RedissonClient redissonClient;
    private final TransactionTemplate transactionTemplate;
    //最大持有锁超时时间: 单位秒
    private static final int MAX_HOLD_TIME = 4;

    private RedissonMultiLock getMultiLock(List<String> lockNames) {
        if (CollectionUtil.isEmpty(lockNames)) {
            throw new IllegalArgumentException("lockNames cannot be empty");
        }
        RLock[] locks = new RLock[lockNames.size()];
        for (int i = 0; i < lockNames.size(); i++) {
            locks[i] = getLock(lockNames.get(i));
        }
        return new RedissonMultiLock(locks);
    }

    private RLock getLock(String lockName) {
        if (StrUtil.isBlank(lockName)) {
            throw new IllegalArgumentException("lockName cannot be empty");
        }
        return redissonClient.getLock(lockName);
    }

    private <T> T executeWithLock(RLock lock, int lockTime, RedissonLockWorker<T> worker) throws Exception {
        long startTime = System.nanoTime();
        String lockName = (lock instanceof RedissonMultiLock) ? "multi-lock" : lock.getName();
        boolean acquired = false;

        try {
            acquired = lock.tryLock(MAX_WAIT_TIME, lockTime, TimeUnit.SECONDS);
            if (!acquired) {
                log.error("线程 {} 未能获取锁 {}", Thread.currentThread().getName(), lockName);
                throw new RedissonLockException("系统业务繁忙,请稍后再试");
            }

            log.info("线程 {} 获取了锁 {}", Thread.currentThread().getName(), lockName);
            return worker.execute();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("线程 {} 获取锁时被中断 {}", Thread.currentThread().getName(), lockName, e);
            throw new RedissonLockException("系统异常终止,请稍后再试");
        } catch (RedissonLockException e) {
            // RedissonLockException 已经记录了日志
            throw e;
        } catch (Exception e) {
            log.error("线程 {} 获取锁时遇到异常 {}", Thread.currentThread().getName(), lockName, e);
            //将异常原封不不动的抛出去
            throw e;
        } finally {
            long endTime = System.nanoTime();
            long holdTime = (endTime - startTime) / 1_000_000;
            //转换成秒
            log.info("线程 {} 持有锁 {} 的时间为 {} 毫秒", Thread.currentThread().getName(), lockName, holdTime);
            if (holdTime > MAX_HOLD_TIME * 1000) {
                log.error("线程 {} 持有锁 {} 的时间超过 {} 毫秒,此方法应该被重点关注", Thread.currentThread().getName(), lockName, MAX_HOLD_TIME);
            }
            if (acquired) {
                try {
                    lock.unlock();
                    log.info("线程 {} 释放了锁 {}", Thread.currentThread().getName(), lockName);
                } catch (Exception e) {
                    log.error("线程 {} 释放锁时失败 {}", Thread.currentThread().getName(), lockName, e);
                }
            }
        }
    }

    private <T> T executeWithTransaction(RLock lock, int lockTime, RedissonLockWorker<T> worker) throws Exception {
        return executeWithLock(lock, lockTime, () -> transactionTemplate.execute(status -> {
            try {
                return worker.execute();
            } catch (Exception e) {
                if (!status.isCompleted()) {
                    status.setRollbackOnly();
                }
                log.error("锁内业务执行失败,事务回滚", e);
                throw new RuntimeException("系统执行异常,请稍后再试");
            }
        }));
    }


    @Override
    public boolean lock(String lockName) throws Exception {
        RLock rLock = getLock(lockName);
        return executeWithLock(rLock, LOCK_EXPIRE, () -> true);
    }


    @Override
    public <T> T lock(String lockName, RedissonLockWorker<T> worker) throws Exception {
        return lock(lockName, LOCK_EXPIRE, worker);
    }


    @Override
    public <T> T lock(String lockName, int lockTime, RedissonLockWorker<T> worker) throws Exception {
        RLock rLock = getLock(lockName);
        return executeWithLock(rLock, lockTime, worker);
    }


    @Override
    public <T> T multiLock(List<String> lockNames, RedissonLockWorker<T> worker) throws Exception {
        return multiLock(lockNames, LOCK_EXPIRE, worker);
    }


    @Override
    public <T> T multiLock(List<String> lockNames, int lockTime, RedissonLockWorker<T> worker) throws Exception {
        RedissonMultiLock multiLock = getMultiLock(lockNames);
        return executeWithLock(multiLock, lockTime, worker);
    }


    @Override
    public void lockVoid(String lockName, RedissonLockWorker<Void> worker) throws Exception {
        lockVoid(lockName, LOCK_EXPIRE, worker);
    }

    @Override
    public void lockVoid(String lockName, int lockTime, RedissonLockWorker<Void> worker) throws Exception {
        RLock rLock = getLock(lockName);
        executeWithLock(rLock, lockTime, () -> {
            worker.execute();
            return null;
        });
    }


    @Override
    public void multiLockVoid(List<String> lockNames, RedissonLockWorker<Void> worker) throws Exception {
        multiLockVoid(lockNames, LOCK_EXPIRE, worker);
    }

    @Override
    public void multiLockVoid(List<String> lockNames, int lockTime, RedissonLockWorker<Void> worker) throws Exception {
        RedissonMultiLock multiLock = getMultiLock(lockNames);
        executeWithLock(multiLock, lockTime, () -> {
            worker.execute();
            return null;
        });
    }


    @Override
    public void unLock(String lockName) {
        RLock rLock = getLock(lockName);
        if (rLock.isLocked()) {
            log.info("Thread {} is releasing the lock {}", Thread.currentThread().getName(), lockName);
            try {
                rLock.unlock();
                log.info("Thread {} released the lock {}", Thread.currentThread().getName(), lockName);
            } catch (Exception e) {
                log.error("Thread {} failed to release lock {}", Thread.currentThread().getName(), lockName, e);
            }
        } else {
            log.error("Thread {} tried to release a lock it does not hold {}", Thread.currentThread().getName(), lockName);
        }
    }

    @Override
    public <T> T lockTransaction(String lockName, RedissonLockWorker<T> worker) throws Exception {
        return lockTransaction(lockName, LOCK_EXPIRE, worker);
    }


    @Override
    public <T> T lockTransaction(String lockName, int lockTime, RedissonLockWorker<T> worker) throws Exception {
        RLock rLock = getLock(lockName);
        return executeWithTransaction(rLock, lockTime, worker);
    }

    @Override
    public <T> T multiLockTransaction(List<String> lockNames, RedissonLockWorker<T> worker) throws Exception {
        return multiLockTransaction(lockNames, LOCK_EXPIRE, worker);
    }

    @Override
    public <T> T multiLockTransaction(List<String> lockNames, int lockTime, RedissonLockWorker<T> worker) throws Exception {
        RedissonMultiLock multiLock = getMultiLock(lockNames);
        return executeWithTransaction(multiLock, lockTime, worker);
    }

    @Override
    public void lockTransactionVoid(String lockName, RedissonLockWorker<Void> worker) throws Exception {
        lockTransactionVoid(lockName, LOCK_EXPIRE, worker);
    }

    @Override
    public void lockTransactionVoid(String lockName, int lockTime, RedissonLockWorker<Void> worker) throws Exception {
        RLock rLock = getLock(lockName);
        executeWithTransaction(rLock, lockTime, () -> {
            worker.execute();
            return null;
        });
    }


    @Override
    public void multiLockTransactionVoid(List<String> lockNames, RedissonLockWorker<Void> worker) throws Exception {
        multiLockTransactionVoid(lockNames, LOCK_EXPIRE, worker);
    }


    @Override
    public void multiLockTransactionVoid(List<String> lockNames, int lockTime, RedissonLockWorker<Void> worker) throws Exception {
        RedissonMultiLock multiLock = getMultiLock(lockNames);
        executeWithTransaction(multiLock, lockTime, () -> {
            worker.execute();
            return null;
        });
    }


}

使用范例

    @Override
    public R distributionGiftPack(DistributionGiftPackDTO dto) {
        if (dto == null) {
            return R.failed("参数不能为空");
        }
        //TODO 其他参数判断
        return redissonLockService.lockTransaction(ActivityRedisKey.AC_KEY + dto.getActivityId(), () -> {
            virtualService.distributionGiftPack(dto.getActivityId(), dto.getCustomId(), dto.getCustomName(), dto.getOrderId(), dto.getGoodsIdList());
            return R.ok();
        });
    }
### Redis 分布式锁Redisson 的实现原理 #### 1. **Redis 分布式锁** Redis 分布式锁的核心思想是通过 `SETNX` 和 `EXPIRE` 来实现加锁操作。在早期版本(Redis 2.6.12之前),由于 `SETNX` 不支持设置过期时间,因此需要分两步完成:先调用 `SETNX` 创建键值对表示锁定状态,再调用 `EXPIRE` 设置过期时间以防止死锁[^3]。 然而这种两步操作无法保证原子性,可能会因网络延迟或其他异常导致锁未成功创建却设置了过期时间。为此,Redis 2.6.12引入了新的命令 `SET key value NX PX milliseconds`,其中 `NX` 表示只有当键不存在时才执行设置操作,`PX` 则用于指定毫秒级的过期时间。这种方式能够在一个命令内完成加锁并设定超时,从而有效解决了上述问题。 #### 2. **Redisson 实现分布式锁** Redisson 是基于 Redis 开发的一个 Java 客户端库,它不仅实现了更高级别的抽象接口,还提供了多种类型的分布式对象和服务功能。对于分布式锁而言,Redisson 提供了一种更加健壮可靠的解决方案——`RLock` 接口及其子类实例化方式。 - **加锁逻辑** Redisson 使用 Lua 脚本来确保整个加锁过程具有原子性。该脚本会检查目标资源是否已被占用;如果未被占用,则尝试获取锁并将当前线程 ID 记录下来作为持有者的唯一标识符[^1]。 - **续命机制** 当某个客户端成功获得锁之后,Redisson 会在后台启动一个定时器任务定期向服务器发送续约请求延长锁的有效期限,直到显式解锁为止。此设计可以避免因长时间运行的任务而导致锁提前失效的情况发生[^2]。 - **自旋重试策略** 如果初次未能取得所需资源,则按照预定义间隔不断重复尝试直至达到最大等待时限或最终放弃争夺控制权。 - **公平性和可靠性保障措施** 在某些特殊情况下(比如网络分区), 可能会出现部分节点认为自己已经拿到了全局唯一的锁,但实际上其他地方也有竞争者存在的情形下, redisson 还特别考虑到了这一点并通过内部复杂的协调算法尽可能减少冲突概率[^4]. #### 性能对比分析 | 特性 | Redis 原生分布锁 | Redisson | |-------------------------|------------------------------------------|----------------------------------| | 加锁效率 | 较高 | 略低 | | 锁安全性 | 存在网络抖动等问题 | 更安全可靠 | | 功能扩展能力 | 单纯提供基础加解鎖功能 | 支持更多特性如自动续租、可重入等 | | 易用程度 | 需要开发者手动处理很多细节 | API 封装良好易于集成 | 从表中可以看出虽然原生态方法简单高效但在实际应用过程中往往面临诸多挑战;而借助第三方工具包则可以在一定程度上弥补这些不足之处. ```java // 示例代码展示如何利用Redisson进行分布式锁管理 import org.redisson.api.RLock; import org.redisson.api.RedissonClient; public class DistributedLockExample { private final RedissonClient redissonClient; public void acquireAndReleaseLock(String lockName) throws InterruptedException{ RLock lock = redissonClient.getLock(lockName); try { boolean isLocked = lock.tryLock(10, TimeUnit.SECONDS); // 尝试获取锁最长等待时间为10秒 if(isLocked){ System.out.println(Thread.currentThread().getName()+" acquired the lock."); Thread.sleep(5000L); // Simulate some work }else{ System.err.println("Failed to get lock after waiting..."); } } finally { if(lock.isHeldByCurrentThread()){ lock.unlock(); System.out.println(Thread.currentThread().getName()+ " released the lock."); } } } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值