积分接口被刷爆,全体一个月降薪20%,这个项目我们遗漏了什么?

前言

有一些往事总是会在酒后提起

我问朋友,在工作上有什么事情是到现在还记忆尤新的?

我朋友一个激灵坐起来,点上一支烟,只见烟头亮了4、5下,才见他吐出一口烟来,说道:“那还真有”

起因

刚参加工作那会做个积分兑换的项目,做完安安稳稳回家过年,哪知道才第三天,就被主管紧急召回,说是捅了大篓子,让我们快来。

回来了才知道项目被灰产盯上,积分被刷,业主业绩没做上去,东西倒是一箱箱往外兑,粗略估计,一天差不多损失上万。

我们赶紧排查,通过积分明细发现,这些人一秒就能获取一次积分,这远远超过了咱们规则的限定值,这肯定是用脚本了,我们后来联系上他,死活不承认。业主希望把负面影响减小到最大,选择不再追究,从我们的合同中扣。

那是我刚毕业做的第一个项目,上来就是项目人员全体降薪一个月,降20%!

俗话说,吃一堑长一智,所以我对这个事情印象还是蛮深的。

问题原因

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

–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());
  }
}
  1. 创建一个分布式锁对象,该分布式锁使用redis的NX命令实现

  2. 随后创建一个3s过期的分布式锁,以便锁住该新增积分的请求

  3. 最后在新增积分执行完后,在finally中释放锁

  4. 最后返回该用户的最终积分

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();
    }
  }
}

ointService 中的添加积分逻辑:

1.首先查询该项目积分规则,查询用户该积分项的积分获取记录总数

2.判断该用户的积分记录总数是否大于 积分规则限定的次数,大于则不处理,返回

3.生成积分记录

4.更新用户总积分

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

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

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

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

处理方案

原因总结一下:

添加积分逻辑处理时间过长

分布式锁超时

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

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

那时候刚毕业,团队也是一个临时组成的班组,没什么经验,工作也忙,忽略了代码Review,最关键的是没做接口并发测试,或者根本没做出来,也没有去做长事务监控,以及频繁请求。”

“那你有什么经验想跟大家分享的?”我问他。

“有!”

他说:“做项目别忘记测试!”

最后:下方这份完整的软件测试视频学习教程已经整理上传完成,朋友们如果需要可以自行免费领取 【保证100%免费】

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值