Java高并发秒杀解决方案

    xiaoxiao2022-07-05  177

    Java高并发秒杀解决方案

     

    ,转载: https://blog.csdn.net/qq_34178598/article/details/84923636

    一.秒杀业务分析

    所谓秒杀,就是网络卖家发布一些超低价格的商品,所有买家在同一时间网上抢购的一种销售方式。

    秒杀商品通常有两种限制:时间限制,库存限制。

    秒杀业务的运行流程主要可以分为以下几点:

    商家提交秒杀商品申请,录入秒杀商品数据,主要有:商品标题,商品原价,秒杀价格,商品图片,介绍等信息运营商审核秒杀申请秒杀频道首页列出秒杀商品,点击秒杀商品图片可以跳转到秒杀商品详细页面商品详细页面显示秒杀商品信息,点击立即抢购实现秒杀下单,下单时扣减库存,当库存为0或者不存在活动时间范围内时无法秒杀秒杀下单成功,直接跳转到支付页面(扫码),支付成功,跳转到成功页面,填写收货、电话、收件人等信息,完成订单。当用户秒杀下单5分钟内未支付,取消预订单,调用支付的关闭订单接口,恢复库存。

    二.数据库设计

    由上述业务流程可知,先要有秒杀商品表,并且不是提交到原来的普通订单表上,要设计一张秒杀订单表,故设计一个秒杀业务至少需要两张表。

    秒杀商品表:秒杀订单表:

    三.秒杀实现思路

    秒杀技术实现核心思想是运用缓存减少数据库瞬间的访问压力。读取商品详细信息时要运用缓存,当用户点击抢购时也要运用缓存,减少缓存中的库存数量,当库存数为0时或活动时间结束才同步到数据库中。产生的秒杀预订单也不会立刻写到数据库中,而是先写到缓存,当用户付款成功后再写入数据库。

    四.实现关键步骤说明

    返回秒杀商品List给前台秒杀频道(创建缓存) //SeckillGoodsServiceImpl.java //获取秒杀商品列表,缓存到Redis中    @Autowired private RedisTemplate redisTemplate; @Override public List<TbSeckillGoods> findList() { //获取秒杀商品列表 List<TbSeckillGoods> seckillGoodsList = redisTemplate.boundHashOps("seckillGoods").values(); if(seckillGoodsList==null || seckillGoodsList.size()==0){ TbSeckillGoodsExample example=new TbSeckillGoodsExample(); Criteria criteria = example.createCriteria(); criteria.andStatusEqualTo("1");//审核通过 criteria.andStockCountGreaterThan(0);//剩余库存大于0 criteria.andStartTimeLessThanOrEqualTo(new Date());//开始时间小于等于当前时间 criteria.andEndTimeGreaterThan(new Date());//结束时间大于当前时间 seckillGoodsList= seckillGoodsMapper.selectByExample(example); //将商品列表装入缓存 System.out.println("将秒杀商品列表装入缓存"); for(TbSeckillGoods seckillGoods:seckillGoodsList){ redisTemplate.boundHashOps("seckillGoods").put(seckillGoods.getId(), seckillGoods); } } return seckillGoodsList; } 123456789101112131415161718192021222324 秒杀详情页(读取缓存) //SeckillGoodsServiceImpl.java //根据秒杀商品id从缓存中获取秒杀商品 @Override public TbSeckillGoods findOneFromRedis(Long id) { return (TbSeckillGoods)redisTemplate.boundHashOps("seckillGoods").get(id); } 123456

    秒杀还剩时间,前台利用商品的start_time和end_time控制。

    秒杀下单(修改缓存) 商品详情页点击立即抢购实现秒杀下单,下单时候扣减库存。当库存为0或者超过活动时间范围内无法秒杀。 //SeckillOrderServiceImpl.java //提交订单 @Autowired private RedisTemplate redisTemplate; @Autowired private IdWorker idWorker; @Override public void submitOrder(Long seckillId, String userId) { //从缓存中查询秒杀商品 TbSeckillGoods seckillGoods =(TbSeckillGoods) redisTemplate.boundHashOps("seckillGoods").get(seckillId); if(seckillGoods==null){ throw new RuntimeException("商品不存在"); } if(seckillGoods.getStockCount()<=0){ throw new RuntimeException("商品已抢购一空"); } //扣减(redis)库存 seckillGoods.setStockCount(seckillGoods.getStockCount()-1); redisTemplate.boundHashOps("seckillGoods").put(seckillId, seckillGoods);//放回缓存 if(seckillGoods.getStockCount()==0){//如果已经被秒光 seckillGoodsMapper.updateByPrimaryKey(seckillGoods);//同步到数据库 redisTemplate.boundHashOps("seckillGoods").delete(seckillId); } //保存(redis)订单 long orderId = idWorker.nextId(); TbSeckillOrder seckillOrder=new TbSeckillOrder(); seckillOrder.setId(orderId); seckillOrder.setCreateTime(new Date()); seckillOrder.setMoney(seckillGoods.getCostPrice());//秒杀价格 seckillOrder.setSeckillId(seckillId); seckillOrder.setSellerId(seckillGoods.getSellerId()); seckillOrder.setUserId(userId);//设置用户ID seckillOrder.setStatus("0");//状态 redisTemplate.boundHashOps("seckillOrder").put(userId, seckillOrder); } 1234567891011121314151617181920212223242526272829303132333435363738 秒杀支付 用户成功下单后,跳转到支付页面,支付页面显示支付二维码。用户完成支付后,保存订单到数据库。 //SeckillOrderServiceImpl.java //根据userId从缓存中获取用户秒杀订单 @Override public TbSeckillOrder searchOrderFromRedisByUserId(String userId) { return (TbSeckillOrder) redisTemplate.boundHashOps("seckillOrder").get(userId); } 123456 //PayController.java /** * 支付控制层 * */ @RestController @RequestMapping("/pay") public class PayController { @Reference private WeixinPayService weixinPayService; @Reference private SeckillOrderService seckillOrderService; /** * 生成二维码 * @return */ @RequestMapping("/createNative") public Map createNative(){ //获取当前用户 String userId=SecurityContextHolder.getContext().getAuthentication().getName(); //到redis查询秒杀订单 TbSeckillOrder seckillOrder = seckillOrderService.searchOrderFromRedisByUserId(userId); //判断秒杀订单存在 if(seckillOrder!=null){ long fen= (long)(seckillOrder.getMoney().doubleValue()*100);//金额(分) return weixinPayService.createNative(seckillOrder.getId()+"",+fen+""); }else{ return new HashMap(); } } } 12345678910111213141516171819202122232425262728293031323334 //SeckillOrderServiceImpl.java //支付成功后保存订单 @Override public void saveOrderFromRedisToDb(String userId, Long orderId, String transactionId) { System.out.println("saveOrderFromRedisToDb:"+userId); //根据用户ID查询日志 TbSeckillOrder seckillOrder = (TbSeckillOrder) redisTemplate.boundHashOps("seckillOrder").get(userId); if(seckillOrder==null){ throw new RuntimeException("订单不存在"); } //如果与传递过来的订单号不符 if(seckillOrder.getId().longValue()!=orderId.longValue()){ throw new RuntimeException("订单不相符"); } seckillOrder.setTransactionId(transactionId);//交易流水号 seckillOrder.setPayTime(new Date());//支付时间 seckillOrder.setStatus("1");//状态 seckillOrderMapper.insert(seckillOrder);//保存到数据库 redisTemplate.boundHashOps("seckillOrder").delete(userId);//从redis中清除 } 1234567891011121314151617181920 //PyController.java //添加查询订单 @RequestMapping("/queryPayStatus") public Result queryPayStatus(String out_trade_no){ //获取当前用户 String userId=SecurityContextHolder.getContext().getAuthentication().getName(); Result result=null; int x=0; while(true){ //调用查询接口 Map<String,String> map = weixinPayService.queryPayStatus(out_trade_no); if(map==null){//出错 result=new Result(false, "支付出错"); break; } if(map.get("trade_state").equals("SUCCESS")){//如果成功 result=new Result(true, "支付成功"); seckillOrderService.saveOrderFromRedisToDb(userId, Long.valueOf(out_trade_no), map.get("transaction_id")); break; } try { Thread.sleep(3000);//间隔三秒 } catch (InterruptedException e) { e.printStackTrace(); } x++;//设置超时时间为5分钟 if(x>100){ result=new Result(false, "二维码超时"); break; } } return result; } 123456789101112131415161718192021222324252627282930313233 订单超时处理 当用户下单后5分钟尚未付款应该释放订单,增加库存。 //SeckillOrderSeriveImpl.java //删除缓存中的订单 @Override public void deleteOrderFromRedis(String userId, Long orderId) { //1.查询出缓存中的订单 TbSeckillOrder seckillOrder = searchOrderFromRedisByUserId(userId); if(seckillOrder!=null){ //2.删除缓存中的订单 redisTemplate.boundHashOps("seckillOrder").delete(userId); //3.库存回退 TbSeckillGoods seckillGoods = (TbSeckillGoods) redisTemplate.boundHashOps("seckillGoods").get(seckillOrder.getSeckillId()); if(seckillGoods!=null){ //如果不为空 seckillGoods.setStockCount(seckillGoods.getStockCount()+1); redisTemplate.boundHashOps("seckillGoods").put(seckillOrder.getSeckillId(), seckillGoods); }else{ seckillGoods=new TbSeckillGoods(); seckillGoods.setId(seckillOrder.getSeckillId()); //属性要设置。。。。省略 seckillGoods.setStockCount(1);//数量为1 redisTemplate.boundHashOps("seckillGoods").put(seckillOrder.getSeckillId(), seckillGoods); } System.out.println("订单取消:"+orderId); } } 1234567891011121314151617181920212223242526272829

    关闭支付宝/微信订单。

    //WeixinPayServiceImpl.java @Override public Map closePay(String out_trade_no) { Map param=new HashMap(); param.put("appid", appid);//公众账号ID param.put("mch_id", partner);//商户号 param.put("out_trade_no", out_trade_no);//订单号 param.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串 String url="https://api.mch.weixin.qq.com/pay/closeorder"; try { String xmlParam = WXPayUtil.generateSignedXml(param, partnerkey); HttpClient client=new HttpClient(url); client.setHttps(true); client.setXmlParam(xmlParam); client.post(); String result = client.getContent(); Map<String, String> map = WXPayUtil.xmlToMap(result); System.out.println(map); return map; } catch (Exception e) { e.printStackTrace(); return null; } } 123456789101112131415161718192021222324 //修改PayController.java /** * 查询支付状态 * @param out_trade_no * @return */ @RequestMapping("/queryPayStatus") public Result queryPayStatus(String out_trade_no){ //获取当前用户 String userId=SecurityContextHolder.getContext().getAuthentication().getName(); Result result=null; int x=0; while(true){ ........ try { Thread.sleep(3000);//间隔三秒 } catch (InterruptedException e) { e.printStackTrace(); } //不让循环无休止地运行定义变量,如果超过了这个值则退出循环,设置时间为1分钟 x++; if(x>20){ result=new Result(false, "二维码超时"); //1.调用微信的关闭订单接口 Map<String,String> payresult = weixinPayService.closePay(out_trade_no); if( !"SUCCESS".equals(payresult.get("result_code")) ){//如果返回结果是正常关闭 if("ORDERPAID".equals(payresult.get("err_code"))){ result=new Result(true, "支付成功"); seckillOrderService.saveOrderFromRedisToDb(userId, Long.valueOf(out_trade_no), map.get("transaction_id")); } } if(result.isSuccess()==false){ System.out.println("超时,取消订单"); //2.调用删除 seckillOrderService.deleteOrderFromRedis(userId, Long.valueOf(out_trade_no)); } break; } } return result; }
    最新回复(0)