使用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()方法,可以处理高并发场景下的问题,也是只有一个线程可以获取锁,但其余线程阻塞等待,直至其它线程释放锁资源,再进行竞争锁资源去执行业务逻辑。
  • 大家根据具体的业务场景,使用合适的方法即可。
相关推荐
ezreal_pan17 分钟前
kafka消费能力压测:使用官方工具
分布式·kafka
宽带你的世界18 分钟前
TiDB 是一个分布式 NewSQL 数据库
数据库·分布式·tidb
xiao-xiang29 分钟前
kafka-集群缩容
分布式·kafka
比花花解语32 分钟前
Kafka在Windows系统使用delete命令删除Topic时出现的问题
windows·分布式·kafka
解决方案工程师33 分钟前
【Kafka】Kafka高性能解读
分布式·kafka
yellowatumn36 分钟前
RocketMq\Kafka如何保障消息不丢失?
分布式·kafka·rocketmq
python资深爱好者1 小时前
什么容错性以及Spark Streaming如何保证容错性
大数据·分布式·spark
HeartRaindj2 小时前
【中间件开发】kafka使用场景与设计原理
分布式·中间件·kafka
明达技术3 小时前
探索分布式 IO 模块网络适配器
分布式
爬山算法5 小时前
Zookeeper(58)如何在Zookeeper中实现分布式锁?
分布式·zookeeper·云原生