【微信支付】关于如何防止用户重复提交订单--分布式锁Redisson
【微信支付】【java】解决问题重复下单问题,使用redis分布式锁解决问题。
我有一篇博文是教大家怎么对接微信小程序对接支付的,然后看到有位答主是这么说:
createOrder有并发问题,同一个用户快速点击下单时可能发出了两次下单请求,会造成在后台生成两行预支付订单。

目录
一、前言
首先非常感谢这位答主的疑惑,在这里我问大家解答下如何防止用户重复提交订单。
原文链接:【微信支付】【java】Springboot对接开发微信支付_wechatpay-java-CSDN博客
二、解决方案
这里使用的是redis分布式锁来解决这个问题,Redisson。其他的不过多介绍,详情可以去官网查看。
三、开始开发
3.1 调试环境
开发语言:Java,框架:SpringBoot,仓库:Maven 3.6.3 及以上,开发工具:Idea
3.2 引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.20.0</version> <!-- 这里版本号可根据实际需求更新 -->
</dependency>
3.3 application.yml文件
spring:
redis:
host: 127.0.0.1
port: 6379
password: your_password
如果是集群,对于集群模式、哨兵模式等,配置会稍有不同:
spring:
redis:
cluster:
nodes:
- 127.0.0.1:7000
- 127.0.0.1:7001
- 127.0.0.1:7002
- 127.0.0.1:7003
- 127.0.0.1:7004
- 127.0.0.1:7005
password: your_password
3.4 可以自定义配置类(可选)
如果你不想用yml文件配置,也可以使用自定义配置文件类来实现(二选一)。
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
// 这里以单机模式为例,可根据实际需求修改为集群、哨兵等模式
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setPassword("your_password");
return Redisson.create(config);
}
}
3.5 使用Redisson
使用方式有多种,我们这里直接用@Autowired 来进行注入。
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LockService {
@Autowired
private RedissonClient redissonClient;
public void doSomethingWithLock() {
RLock lock = redissonClient.getLock("myLock");
try {
// 尝试获取锁,等待时间10秒,锁自动释放时间20秒
boolean isLocked = lock.tryLock(10, 20, TimeUnit.SECONDS);
if (isLocked) {
// 获取到锁后执行的业务逻辑
System.out.println("获取到锁,执行业务逻辑");
} else {
System.out.println("未获取到锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 无论是否获取到锁,最终都要释放锁
lock.unlock();
}
}
}
上面是使用案例,那么问题回到微信支付这块,如何防止用户重复提交呢?
一般支付流程是:选择商品 ->进行下单 ->生成订单信息 ->然后支付。那么在提交支付的接口中是有用户id和订单orderId的,其他参数比如优惠券id我们暂时不考虑。那么按照这种情况来。
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class wxService {
@Autowired
private final RedissonClient redissonClient;
public Boolean createOrder(Long userId,Long orderId) {
//首先定义锁key
String orderKey="wxService.createOrder::"+"userId:"+userId+",orderId:"+orderId;
RLock lock = redissonClient.getLock(orderKey);
try {
// 尝试获取锁,等待时间10秒,锁自动释放时间30秒
boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (!isLocked) {
//相同的key再次加锁的时候,会返回false,你们可以直接抛出异常
System.out.println("操作频繁,未获取到锁");
Asserts.fail("操作频繁");
return false;
}
// 这里开始处理订单信息
xxService.createOrder(xxx);
} catch (InterruptedException e) {
log.error("错误信息"+e.getMessage());
} finally {
// 无论是否获取到锁,最终都要释放锁
if (lock != null && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
解释:我们将用户的id和订单的orderId作为redis分布式锁的key,当用户连续点击两次或者多次的时候,因为第一次已经加了锁了,所以再次加锁的时候就会失败,也就是不走下面订单支付这个动作了。我这里只是提供了个案例,具体还需要根据自己的业务来进一步处理。
3.6 如果任务一直没处理完怎么办?
其实我们在设置key的有效期是30s,那么如果30s内,任务没有处理完怎么办?然后30s后再次提交订单不就又重复提交了吗?
其实不用担心,redisson内部提供了一个看门狗机制,每10s钟就会监控线当前线程持有锁的状态,要是还在,就会进行key续费(续存活时期)。
四、有问题留言
如果你在微信支付期间遇到什么问题,可以在此博文下留言。

点个关注,不会迷路!
更多推荐



所有评论(0)