自学内容网 自学内容网

使用Redisson实现分布式锁解决幂等问题

业务场景

功能:实现创建订单功能,要求是保证接口幂等。

引入pom依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.13.2</version>
</dependency>

注入RedissonClient

@Resource
private RedissonClient redissonClient;

配置文件

application-dev.yml

spring:
  redis:
    redisson:
      config: classpath:redisson-dev.yml
    host: 192.168.0.45
    port: 6379

redisson-dev.yml

singleServerConfig:
  idleConnectionTimeout: 10000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  password: null
  subscriptionsPerConnection: 5
  clientName: null
  address: "redis://192.168.0.45:6379"
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  connectionMinimumIdleSize: 10
  connectionPoolSize: 64
  database: 0
  dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0

代码实现

@Override
    public void createAppPayOrders(Long userId, String orderNo) {
        // 使用Redis的分布式锁(处理幂等问题)
        String lockKey = "app_pay_lock_" + orderNo;
        RLock lock = redissonClient.getLock(lockKey);
        if (!lock.tryLock()) {
            return;
        }

        try {
            // 处理业务逻辑
            // ......
        } catch (Exception e) {
            log.error("error, userId:{}, orderNo:{}", userId, orderNo, e);
        } finally {
            // 释放锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

Redisson相关方法介绍

Redisson框架是对Redis分布式锁进行的封装,其底层有lua脚本,可以保证加锁和解锁的原子性,并且内置watchDog看门狗机制,支持锁续期,解决了原始Redis实现分布式锁时出现的锁过期释放,业务没执行完的问题。

  • getLock(key): 通过key名称返回 Lock 的实例,要注意的getLock()是实现非公平锁定,因此不保证线程的获取顺序。
  • lock():获得锁,如果锁不可用,则当前线程将被禁用以用于线程调度目的并处于休眠状态,直到获得锁为止。此操作可能会导致死锁发生,所以我们可以使用以下的lock()方法
  • lock(long 等待时间, TimeUnit 时间单位): 使用定义的等待时间获取锁。如有必要,等待锁定可用。锁定将在定义的等待时间间隔后自动释放。如果等待时间为-1,则保持锁定直到被解锁。和lockInterruptibly() 方法差不多。
  • tryLock():尝试获取锁,并立即返回获取锁的结果(true 或 false),如果有可用锁返回 true,并得到此锁;如果没有可用锁会立即返回 false。
  • tryLock(long 超时时间, TimeUnit 时间单位):第一个参数是 long 类型的超时时间,第二个参数是对参数一的时间类型描述(比如第一参数是 3,那么它究竟是 3 秒还是 3 分钟,是第二个参数说了算的)。在这段时间内如果获取到可用的锁了就返回 true,如果在定义的时间内,没有得到锁就会返回 false。
  • isLocked(): 检查锁是否被任何线程锁定,如果锁定返回true,否则返回 false。
  • isHeldByCurrentThread(): 检查此锁是否由当前线程持有,如果持有,返回 true,否则为 false。
  • unLock():释放锁。Lock 实现通常会对哪个线程可以释放锁施加限制(通常只有锁的持有者可以释放它)并且如果违反限制可能会抛出 unchecked 异常。该 Lock 实现必须记录任何限制和异常类型。

小结

  • 本文主要使用Redisson的tryLock()方法,拿到锁的线程去执行业务逻辑,其余的线程拿不到锁就放行,不需要阻塞,因为我们的目的是保证接口幂等。
  • 如果使用Redisson的lock()方法,可以处理高并发场景下的问题,也是只有一个线程可以获取锁,但其余线程阻塞等待,直至其它线程释放锁资源,再进行竞争锁资源去执行业务逻辑。
  • 大家根据具体的业务场景,使用合适的方法即可。

原文地址:https://blog.csdn.net/qq_37896194/article/details/137525141

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!