当前位置: 首页 > 科技观察

Java秒杀系统:实战商品秒杀代码

时间:2023-03-16 14:19:20 科技观察

内容:“商品秒杀”功能模块基于“商品详情”功能模块。对于该功能模块,其主要核心流程为:前端发起抢购请求,请求中会携带一些请求数据:等待秒杀Id和当前用户Id等数据;后端接口收到请求后,会执行一系列的判断和秒杀处理逻辑,最后将处理结果返回给前端。其中,后台接口的一系列判断和秒杀处理逻辑比较复杂,Debug将其画成如下流程图:从业务流程图可以看出,后台接口正在接收秒杀请求从前端用户来说,核心处理逻辑是:(1)首先判断当前用户是否抢购过商品,如果没有,则说明用户没有抢购过商品,可以进入下一个处理逻辑(2)判断商品是否可以抢购剩余数量,即库存是否充足(即是否大于0),如果是则进入下一步处理逻辑(3)扣除库存,并更新数据库中抢购记录对应的库存(一般减去一次操作),判断数据库更新库存操作是否成功,如果成功则创建一个成功订单供用户秒杀,以及发简讯或邮件通知信息异步通知用户(4)如果上述操作逻辑中有哪一步不满足条件,则直接结束整个秒杀流程,即秒杀失败!接下来,我们还是基于MVC的开发模式,用实际的代码来实现这个功能模块!(1)首先在KillController控制器中开发接收“前端用户秒杀请求”的函数方法。其中,该方法需要接收前端请求的“等待秒杀Id”,当前用户的Id可以通过上一篇博文Shiro的会话模块Session来获取!其源码如下:privatestaticfinalStringprefix="kill";@AutowiredprivateIKillServicekillService;@AutowiredprivateItemKillSuccessMapperitemKillSuccessMapper;/****商品秒杀核心业务逻辑*/@RequestMapping(value=prefix+"/execute",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_UTF8_VALUE)@ResponseBodypublicBaseResponseexecute(@RequestBody@ValidatedKillDtodto,BindingResultresult,HttpSessionsession){if(result.hasErrors()||dto.getKillId()<=0){returnnewBaseResponse(StatusCode.InvalidParams);}//获取当前登录用户的信息ObjectuId=session.getAttribute("uid");if(uId==null){returnnewBaseResponse(StatusCode.UserNotLogin);}IntegeruserId=(Integer)uId;BaseResponseresponse=newBaseResponse(StatusCode.Success);try{Booleanres=killService.killItem(dto.getKillId(),userId);if(!res){returnnewBaseResponse(StatusCode.Fail.getCode(),"哈哈~商品已被抢购或不在抢购期!");}}catch(Exceptione){response=newBaseResponse(StatusCode.Fail.getCode(),e.getMessage());}returnresponse;}复制代码其中KillDto对象主要封装了“PendingseckillId”等字段信息,主要用于接收用户秒杀请求来自前端的信息,源码如下:@Data@ToStringpublicclassKillDtoimplementsSerializable{@NotNullprivateIntegerkillId;privateIntegeruserId;//集成shiro后,userId字段不用了!因为}复制代码(2)是通过session获取的,后面开发了killService.killItem(dto.getKillId(),userId)函数,该函数对应的代码编写逻辑编辑可以参考本文开头的流程图!其完整源码如下:@AutowiredprivateItemKillSuccessMapperitemKillSuccessMapper;@AutowiredprivateItemKillMapperitemKillMapper;@AutowiredprivateRabbitSenderServicerabbitSenderService;//处理商品秒杀的核心业务逻辑@OverridepublicBooleankillItem(IntegerkillId,TOegeruserId)throwsException/{Boolesean;当前商品if(itemKillSuccessMapper.countByKillUserId(killId,userId)<=0){//TODO:查询秒杀商品详情ItemKillitemKill=itemKillMapper.selectById(killId);//TODO:判断是否可以被秒杀秒杀canKill=1?if(itemKill!=null&&1==itemKill.getCanKill()){//TODO:扣除库存-减一intres=itemKillMapper.updateKillItem(killId);//TODO:是否扣除成功?是-生成秒杀成功命令,同时通知用户秒杀成功消息if(res>0){commonRecordKillSuccessInfo(itemKill,userId);result=true;}}}else{thrownewException("Youhavealready抢购此商品!”);}returnresult;}复制代码where,itemKillMapper.selectById(killId);表示用于秒级获取待售商品的详细信息,上一章介绍过;和itemKillMapper.updateKillItem(killId);主要用于扣除库存(这里是减1操作),对应的动态Sql如下:UPDATEitem_killSETtotal=total-1WHEREid=#{killId}复制代码(3)值得一提的是,上面的KillService在执行killItem函数方法时,也开发了一个通用方法:用户kill后成功创建秒杀订单,并异步向用户发送秒杀成功的通知消息!方法为commonRecordKillSuccessInfo(itemKill,userId);其完整源码如下:/***通用方法-创建订单后用户秒杀成功-并通知异步邮件消息*@paramkill*@paramuserId*@throwsException*/privatevoidcommonRecordKillSuccessInfo(ItemKillkill,IntegeruserId)throwsException{//TODO:记录抢购成功后产生的秒杀订单记录ItemKillSuccessentity=newItemKillSuccess();StringorderNo=String.valueOf(snowFlake.nextId());//entity.setCode(RandomUtil.generateOrderCode());//传统时间戳+N位随机数entity.setCode(orderNo);//雪花算法entity.setItemId(kill.getItemId());entity.setKillId(kill.getId());entity.setUserId(userId.toString());entity.setStatus(SysConstant.OrderStatus.SuccessNotPayed.getCode().byteValue());entity.setCreateTime(DateTime.now().toDate());//TODO:学以致用,举一反三->像单例模式的double-check锁写法if(itemKillSuccessMapper.countByKillUserId(kill.getId(),userId)<=0){intres=itemKillSuccessMapper.insertSelective(entity);if(res>0){//TODO:Notifyasynchronousemailmessages=rabbitmq+mailrabbitSenderService.sendKillSuccessEmailMsg(orderNo);//TODO:超过指定顺序的“失败”进入死信队列TTL时间还未支付rabbitSenderService.sendKillSuccessOrderExpireMsg(orderNo);}}}复制代码该方法涉及到的功能模块稍微多一点,即主要包括“分布式唯一ID-Snowflake算法应用”、“异步集成RabbitMQ”给用户发送通知消息”、“开发基于JavaMail发送邮件的功能”、“死信队列过期超时未付订单”等,这些功能模块将在后面的章节中逐步介绍!(4)最后需要在前端页面info.jsp上开发“提交用户秒杀请求”的功能,其部分核心源码如下:其中,提交的数据在formatofapplication/json,即json格式!并使用POST请求方式进行交互!(5)用外部tomcat运行整个系统和项目,观察控制台输出信息。如果没有报错信息,说明整个实战代码没有语法级的错误!点击“详情”按钮,登录成功后,进入“秒杀商品详情”,可以查看当前秒杀商品详情;点击“购买”按钮进入“秒杀”链接。逻辑处理后,将处理结果返回给前端,如下图:同时,当前用户的邮箱会收到“秒杀成功”的邮件信息,表示当前用户已成功抓取当前商品,如下图:另外,在数据库表item_kill_success中还会生成一条“秒杀成功的订单记录”,如下图:当然,对于“邮件通知”和“秒杀成功”成功生成订单订单号”功能,本节我们主要如果在秒杀系统中分享用户的“秒杀/求购”功能!