2024-08-27 早上测试提交BUG,说售后单状态流转不对,吓得我一激灵,赶紧打开IDEA 查看代码,发现售后这块代码没有动过呀,咋回事?
流程是这样的: 测试模拟用户下单,提交订单后付款,然后用户又进行退款操作,需要商家审核售后,商家审核通过的话,进行退款操作,售后单更新状态售后完结。
然后这个单据进行了退款操作,但是售后单状态没有完结。
自己又顺着测试的步骤操作了一遍,也没有复现问题,测试自己也不能没有这个问题。
没有办法,硬着头皮下载日志回来看看吧!
这两个单都是未发货退款,500002280004 正常售后退款成功,
500002280003 退款成功但是售后状态不对
直接在日志里面查询售后单号:
发现这个售后单在不到 0.1秒 请求了两次,整个售后退款流程代码一秒才完成
即使在代码对售后状态进行了校验也是无用,因为并发请求时,上一个售后代码没有执行完成,事务没有提交,新来的请求读取的售后状态也是未完结的,可以继续往下执行代码,
执行到退款代码时,该同学意识到可能重复退款,加了一把分布式锁,也算不幸中的万幸,没有造成重复退款
那么我现在要做的就是在在售后接口进行防重,先说一下方案,就是在入口处添加一把分布式锁,获取到锁就往下执行,代码执行完成,删除锁,防止并发重复请求。
详细分布式锁使用可以看我的另一篇文章:
AOP自定义注解防重-CSDN博客
再来说一下幂等和防重的概念:
幂等性和防重复是确保软件系统正确性的重要概念。下面通过一些具体的例子来解释这两个概念:
### 幂等性(Idempotence)
1. HTTP GET 请求:当你对一个 HTTP GET 请求进行多次调用时,比如请求一个网页,无论请求多少次,返回的网页内容都是相同的。这就是幂等性的体现。
2. 数据库更新操作:假设有一个 SQL 更新语句 `UPDATE users SET age = 30 WHERE id = 1`。无论这个语句执行一次还是多次,只要条件 `id = 1` 不变,用户1的年龄都会被设置为30岁。
3. 设置键值对:在 Redis 中使用 `SET` 命令设置一个键值对是幂等的。比如 `SET key value`,无论执行多少次,只要键名不变,键对应的值都会是最后一次设置的值。
4. 删除操作:删除一个文件或数据库记录的操作也是幂等的。无论执行多少次,文件或记录只会被删除一次。
### 防重复(Idempotent Message Processing)
1. 订单系统中的唯一订单号:在订单系统中,每个订单都有一个唯一的订单号。当处理订单时,系统会检查订单号是否已经存在,如果存在,则不会重复处理同一个订单。
2. 消息队列中的消息唯一标识符:在消息队列中,每条消息都有一个全局唯一的标识符。消费者在处理消息前会检查这个标识符,如果已经处理过,则不会再次处理。
以下是一些具体的例子:
1. 支付系统:在支付系统中,每次支付请求都会生成一个唯一的交易ID。即使支付请求因为网络问题被重复发送,系统也会通过检查交易ID来避免重复扣款。
2. 银行转账:当你通过银行应用进行转账时,每次转账操作都会生成一个唯一的交易号。即使你多次点击“确认转账”,银行系统也会通过交易号来确保转账操作只执行一次。
3. 社交媒体发帖:在社交媒体平台上,当你发布一条帖子时,即使因为网络延迟你多次点击“发布”,平台也会通过检查帖子的唯一标识符来确保你的帖子只被发布一次。
4. 邮件发送:邮件发送系统通常会为每封邮件分配一个唯一的消息ID。即使因为某些原因邮件发送请求被重复触发,系统也会通过消息ID来避免重复发送同一封邮件。
通过这些例子,我们可以看到幂等性确保了操作重复执行不会产生副作用,而防重复机制则是通过系统设计来避免操作被重复执行。