业务场景
功能:实现创建订单功能,要求是保证接口幂等。
引入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()方法,可以处理高并发场景下的问题,也是只有一个线程可以获取锁,但其余线程阻塞等待,直至其它线程释放锁资源,再进行竞争锁资源去执行业务逻辑。
- 大家根据具体的业务场景,使用合适的方法即可。