因为锁的问题,我们被扣了1万

2年前未命名67
因为锁的问题,我们被扣了1万 阿提说说 已于2023-02-14 18:03:35修改 2867 收藏 26 分类专栏: 疑难杂症 文章标签: redis java 分布式锁 于2023-02-14 17:53:39首次发布 疑难杂症 专栏收录该内容 4 篇文章 2 订阅 订阅专栏 前言

春节放假期间,一个项目上的积分接口被刷,而且不止一个人在刷,并且东西也被兑走,放假晚上被人叫起来排查问题,通过这个人的积分明细观察,基本一秒就能获取一次,远远超过了积分规则限定的次数,这肯定是用脚本了,虽然后期联系死活说自己是正常途径获取。由于是业主,我们还是决定自己来承担这个损失,被项目方从合同中扣除奖品费用1万余元。

问题原因

先说下接口的逻辑层次结构:

–controller 积分获取接口,用PointController表示

–service 积分获取接口service,用PointService表示

我用伪代码来表示整个调用逻辑:

PointController

@RestController public class PointController { @Resource private PointService pointService; @GetMapping(/addPoint) public Response addPoint() { //分布式锁,使用redis的NX命令 RedisDistributedLock lock = new RedisDistributedLock(); //创建一个3s过期,100ms休眠的锁 if(lock.lock("POINT_KEY", 3000L, 100L)) { try { //调用 pointService.addPoint(); } catch (Exception ex) { e.printStackTrace; } finally { //解锁 lock.unlock("POINT_KEY"); } } return Response.ok(getLastPoint()); } } 创建一个分布式锁对象,该分布式锁使用redis的NX命令实现随后创建一个3s过期的分布式锁,以便锁住该新增积分的请求最后在新增积分执行完后,在finally中释放锁最后返回该用户的最终积分

PointService

public class PointService { @Transactional(rollbackFor = Exception.class) public void addPoint() { //查询积分规则 PointRule pointRule = getPointRule(); //查询用户该积分项的积分获取记录总数 Integer total = getPointRecords(); //判断该用户的积分记录总数是否大于 积分规则限定的次数 //大于则不处理,返回 if(total - pointRule.getRuleTimes >= 0) { return; } //生成积分记录 int insert = insertPointRecords(); //更新用户总积分 if(insert > 0) { updateUserPoint(); } } }

PointService 中的添加积分逻辑:

首先查询该项目积分规则,查询用户该积分项的积分获取记录总数判断该用户的积分记录总数是否大于 积分规则限定的次数,大于则不处理,返回生成积分记录更新用户总积分

该添加积分的逻辑整体上看好像没什么问题,也确实在一切正常的情况下运行是不会有问题的。

如果PointService 中的添加积分逻辑在分布式锁有效期3s内执行完,是不会有问题的。

但如果PointService中的添加积分逻辑超过3s…那是不是后续请求又可以获取锁了,这也正是这次事故的原因。

因为PointService中的添加积分逻辑超过了3s,并且上一个请求的事务还未提交,后续请求已经获取锁进入PointService,在查询积分记录后,判断还是满足规则,继续执行后续的逻辑,造成用户能够获取多次积分。

问题处理

原因总结一下:

添加积分逻辑处理时间过长分布式锁超时

第一个问题:逻辑改动过大,需要时间调整,没有采用

第二个问题:换成Redisson,因为redisson在即使超时的情况下也会续锁,避免锁超时

总结

一方面因为忙于做项目,忽略了代码Review,另一方面测试的时候没有对接口进行并发测试,或者根本没有测出来,第三没有监控工具监控长事务,以及频繁请求。

作者其他文章: 《Prometheus+Grafana 实践派》专栏火热更新中

Grafana 的介绍和安装Grafana监控大屏配置参数介绍(一)Grafana监控大屏配置参数介绍(二)Grafana监控大屏可视化图表Grafana 查询数据和转换数据Grafana 告警模块介绍Grafana 告警接入飞书通知

Spring Boot Admin 系列

Spring Boot Admin 参考指南SpringBoot Admin服务离线、不显示健康信息的问题Spring Boot Admin2 @EnableAdminServer的加载Spring Boot Admin2 AdminServerAutoConfiguration详解Spring Boot Admin2 实例状态监控详解Spring Boot Admin2 自定义JVM监控通知Spring Boot Admin2 自定义异常监控Spring Boot Admin 监控指标接入Grafana可视化
标签: [db:标签TAG]

相关文章

搭建hadoop高可用集群(二)

搭建hadoop高可用集群(二)...

docker引擎架构

docker引擎架构...

前端处理并发的最佳实践

前端处理并发的最佳实践...

TCP协议原理一

TCP协议原理一...

ChatGPT背后的经济账

ChatGPT背后的经济账...

ChatGPT 为我制作了一张地图

ChatGPT 为我制作了一张地图...