因为锁的问题,我们被扣了1万
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
前言
春节放假期间一个项目上的积分接口被刷而且不止一个人在刷并且东西也被兑走放假晚上被人叫起来排查问题通过这个人的积分明细观察基本一秒就能获取一次远远超过了积分规则限定的次数这肯定是用脚本了虽然后期联系死活说自己是正常途径获取。由于是业主我们还是决定自己来承担这个损失被项目方从合同中扣除奖品费用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 系列