SpringMVC集成rabbitmq:优化秒杀下单环节
2019-02-17 01:49:45来源:博客园 阅读 ()
前言
上一篇在springboot中基于自动配置集成了rabbitmq。那么回到最初的话题中就是想在秒杀下单环节增加排队机制,从而达到限流的目的。
优化秒杀下单流程
之前是在控制器里拿到客户端请求后直接入库、减库存。如果碰到羊毛党其实这套机制是不行的。并发量高的时候,库存数量也会不准确。那么引入rabbitmq则在下单时让用户信息产生一条消息入队。然后消费者处理下单(是否重复下单、下单失败、库存不够)。客户端接受到请求已入队列(response引入state处理交互)后发起ajax轮询请求,处理成功则跳转下单成功页或者结束本次交互。
1、下单(秒杀接口)
@RequestMapping(value="/{seckillId}/{md5}/execute",method = RequestMethod.POST,produces = {"application/json;charset=UTF-8"}) @ResponseBody public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId")Long seckillId, @PathVariable("md5")String md5, @CookieValue(value="phone",required=false)Long phone){ if(phone==null){ return new SeckillResult<SeckillExecution>(false,"手机号未注册"); } SeckillResult<SeckillExecution> result=null; try{ SeckillExecution execution=seckillService.executeSeckill(seckillId,phone,md5); result=new SeckillResult<SeckillExecution>(true,execution); }catch(RepeatKillException e){ SeckillExecution execution=new SeckillExecution(seckillId,-1,"重复秒杀"); result=new SeckillResult<SeckillExecution>(true,execution); }catch(SeckillCloseException e){ SeckillExecution execution=new SeckillExecution(seckillId,0,"秒杀结束"); result=new SeckillResult<SeckillExecution>(true,execution); }catch (Exception e){ SeckillExecution execution=new SeckillExecution(seckillId,-2,"系统异常"); result=new SeckillResult<SeckillExecution>(true,execution); } return result; }
2、下单业务方法(Service) 这里就要引入排队
@Override public SeckillExecution executeSeckill(long seckillId, long phone, String md5) throws SeckillException,RepeatKillException,SeckillCloseException { if (md5 == null || !md5.equals(getMd5(seckillId))) { throw new SeckillException("非法请求"); } Date now = new Date(); try { int insertCount = successKillDao.insertSuccessKilled(seckillId, phone); if (insertCount <= 0) { throw new RepeatKillException("重复秒杀"); } else { //请求入队 MiaoshaUser miaoshaUser=new MiaoshaUser(); miaoshaUser.setPhone(phone); MiaoshaMessage miaoshaMessage=new MiaoshaMessage(); miaoshaMessage.setSeckillId(seckillId); miaoshaMessage.setMiaoshaUser(miaoshaUser); String miaosha=JSON.toJSONString(miaoshaMessage); amqpTemplate.convertAndSend(miaosha); return new SeckillExecution(seckillId,0,"请求入队"); /*** * 直接入库操作 int updateCount = seckillDao.reduceNumber(seckillId, now); if (updateCount <= 0) { throw new SeckillCloseException("秒杀已关闭"); } else { //秒杀成功,可以把秒杀详情和商品详情实体返回 SuccessKilled successKilled = successKillDao.queryByIdWithSeckill(seckillId, phone); return new SeckillExecution(seckillId, 1, "秒杀成功", successKilled); } ***/ } } catch (SeckillCloseException e) { throw e; } catch (RepeatKillException e1) { throw e1; } catch (SeckillException e2) { logger.error(e2.getMessage(), e2); throw new SeckillException("Unkonwn error:" + e2.getMessage()); } }
3、下单结果接口
@RequestMapping(value="/{seckillId}/{md5}/result",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"}) @ResponseBody public SeckillResult<SeckillExecution> result(@PathVariable("seckillId")Long seckillId, @PathVariable("md5")String md5, @CookieValue(value="phone",required=false)Long phone){ SuccessKilled successKilled = seckillService.queryByIdWithSeckill(seckillId, phone); SeckillExecution execution=null; if(successKilled.getSeckillId()>0){ execution=new SeckillExecution(seckillId, 1, "下单成功", successKilled); }else{ execution=new SeckillExecution(seckillId, -2, "下单失败", successKilled); } return new SeckillResult<SeckillExecution>(true,execution); }
4、消费者(下单处理)
/** * 秒杀请求消费 **/ public class AmqpConsumer implements MessageListener { @Autowired SeckillDao seckillDao; @Autowired SuccessKillDao successKillDao; @Override public void onMessage(Message message) { Date now = new Date(); MiaoshaMessage miaosha = JSON.parseObject(message.getBody(), MiaoshaMessage.class); Long seckillId = miaosha.getSeckillId(); int updateCount = seckillDao.reduceNumber(seckillId, now); if (updateCount <= 0) { System.out.println("秒杀下单失败"); } else { System.out.println("秒杀下单成功"); } } }
5、springmvc集成消息队列配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd"> <!--spring集成rabbitmq--> <rabbit:connection-factory id="connectionFactory" host="192.168.80.128" port="5672" username="admin" password="admin" channel-cache-size="5" virtual-host="/" /> <rabbit:admin connection-factory="connectionFactory"/> <!--声明队列--> <rabbit:queue durable="true" auto-delete="false" exclusive="false" name="miaosha.queue"/> <!--交换器和队列绑定--> <rabbit:direct-exchange name="miaosha.exchange"> <rabbit:bindings> <rabbit:binding queue="miaosha.queue" key="miaosha.tag.key"/> </rabbit:bindings> </rabbit:direct-exchange> <!--spring rabbitmqTemplate声明--> <rabbit:template id="rabbitTemplate" exchange="miaosha.exchange" routing-key="miaosha.tag.key" connection-factory="connectionFactory" /> <!--消息监听--> <bean id="miaoshaConsumer" class="com.seckill.mq.AmqpConsumer"/> <rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto"> <rabbit:listener ref="miaoshaConsumer" queues="miaosha.queue"/> </rabbit:listener-container> </beans>
6、客户端秒杀下单、等待下单结果
/**秒杀结果**/ miaosha:function(seckillId,md5,node){ $.get('/seckill/'+seckillId+'/'+md5+'/result',{},function(result){ if(result && result["success"]){ var oData=result["data"]; if(oData["state"]===1){ node.html("<span class='label label-success'>下单成功</span>"); clearInterval(miaoshaTimer); }else{ console.log("还在排队种..."); } } }) }, /**执行秒杀**/ seckill:function(seckillId,node){ //获取秒杀地址、控制node节点显示,执行秒杀 node.hide().html("<button id='killBtn' class='btn btn-primary btn-lg'>开始秒杀</button>") $.get('/seckill/'+seckillId+'/exposer',{},function(result){ if(result && result["success"]){ //在回调函数中执行秒杀操作 var exposer=result["data"]; if(exposer["exposed"]){ //秒杀已开始 var md5=exposer["md5"]; var killUrl='/seckill/'+seckillId+'/'+md5+'/execute'; console.log(killUrl); $("#killBtn").one('click',function(){ //1、禁用秒杀按钮 $(this).addClass('disabled'); //2、执行秒杀操作 $.post(killUrl,{},function(result){ if(result && result["success"]){ var killResult=result["data"]; var state=killResult["state"]; var stateInfo=killResult["stateInfo"]; node.html("<span class='label label-success'>"+stateInfo+"</span>"); if(state===0){ //已入队,客户端开始轮询 miaoshaTimer=setInterval(function(){ seckill.miaosha(seckillId,md5,node); },3000); } } }) }); node.show(); }else{ //秒杀未开始, 防止浏览器和服务器出现时间差,再次执行倒数计时 var now = exposer['now']; var start = exposer['start']; var end = exposer['end']; seckill.countdown(seckillId, now, start, end); } }else{ console.log('result:'+result); //没有拿到秒杀地址 } }) }
好了,贴了这么多代码,没有示意图怎么能行?
总结
秒杀下单增加排队机制来说对于完整的秒杀系统来说只是其中很少的一部分,这里也只是学习rabbitmq的一个过程。对于秒杀系统来说流量主要是查询多下单少。还需要引入redis,把库存量、商品信息能在秒杀开始前预处理。
参考资料
https://blog.csdn.net/sunweiguo1/article/details/80470792
原文链接:https://www.cnblogs.com/sword-successful/p/10368820.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- springboot2配置JavaMelody与springMVC配置JavaMelody 2020-06-11
- 微服务项目持续集成部署流程简介 2020-05-27
- 蚂蚁金服这套SpringMvc面试题你懂多少(面试必刷) 2020-05-27
- SpringBoot2.3整合RabbitMQ实现延迟消费消息 2020-05-26
- Spring Boot 开发集成 WebSocket,实现私有即时通信系统 2020-05-24
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash