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

业务场景

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

引入pom依赖

xml 复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.13.2</version>
</dependency>

注入RedissonClient

java 复制代码
@Resource
private RedissonClient redissonClient;

配置文件

application-dev.yml

yml 复制代码
spring:
  redis:
    redisson:
      config: classpath:redisson-dev.yml
    host: 192.168.0.45
    port: 6379

redisson-dev.yml

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

代码实现

java 复制代码
	@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()方法,可以处理高并发场景下的问题,也是只有一个线程可以获取锁,但其余线程阻塞等待,直至其它线程释放锁资源,再进行竞争锁资源去执行业务逻辑。
  • 大家根据具体的业务场景,使用合适的方法即可。
相关推荐
spiker_3 小时前
RabbitMQ 常见使用模式详解
分布式·rabbitmq
不能再留遗憾了3 小时前
RabbitMQ 高级特性——持久化
分布式·rabbitmq·ruby
成为大佬先秃头4 小时前
解决RabbitMQ设置TTL过期后不进入死信队列
分布式·中间件·rabbitmq·java-rabbitmq
七夜zippoe6 小时前
分布式系统实战经验
java·分布式
nomi-糯米7 小时前
Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约
分布式·网络安全·区块链·智能合约·分布式账本
喜欢猪猪7 小时前
Kafka是如何保证数据的安全性、可靠性和分区的
分布式·kafka
芊言芊语7 小时前
分布式消息服务Kafka版的详细解析和配置方式
分布式·kafka
Alluxio7 小时前
选择Alluxio来解决AI模型训练场景数据访问的五大理由
大数据·人工智能·分布式·ai·语言模型
武子康8 小时前
大数据-133 - ClickHouse 基础概述 全面了解
java·大数据·分布式·clickhouse·flink·spark
.生产的驴8 小时前
SpringBoot 消息队列RabbitMQ 消费者确认机制 失败重试机制
java·spring boot·分布式·后端·rabbitmq·java-rabbitmq