▶【SecKill】U5 页面优化技术


?【SecKill】U5 页面优化技术

一、页面缓存+URL缓存+对象缓存

function doMiaosha(){ $.ajax({ url:"/miaosha/do_miaosha", type:"POST", data:{ goodsId:$("#goodsId").val(), //获取goodsId }, success:function(data){ if(data.code == 0){ window.location.href="/order_detail.htm?orderId="+data.data.id; }else{ layer.msg(data.msg); } }, error:function(){ layer.msg("客户端请求有误"); } }); } //1.商品详情静态化:渲染页面 function render(detail){ var miaoshaStatus = detail.miaoshaStatus; var remainSeconds = detail.remainSeconds; var goods = detail.goods; var user = detail.user; if(user){ //若已登录,能获取到用户,就把提示信息隐藏 $("#userTip").hide(); } $("#goodsName").text(goods.goodsName); $("#goodsImg").attr("src", goods.goodsImg); $("#startTime").text(new Date(goods.startDate).format("yyyy-MM-dd hh:mm:ss")); $("#remainSeconds").val(remainSeconds); $("#goodsId").val(goods.id); $("#goodsPrice").text(goods.goodsPrice); $("#miaoshaPrice").text(goods.miaoshaPrice); $("#stockCount").text(goods.stockCount); countDown(); } //页面初始化 $(function(){ //countDown(); getDetail(); }); //1.商品详情静态化 function getDetail(){ var goodsId = g_getQueryString("goodsId"); //获取url参数(common.js) $.ajax({ url:"/goods/detail/"+goodsId, type:"GET", success:function(data){ if(data.code == 0){ //成功 render(data.data); //根据服务端的数据,把页面渲染出了 }else{ //失败 layer.msg(data.msg); //把错误消息打印出来 } }, error:function(){ layer.msg("客户端请求有误"); } }); } //秒杀倒计时 function countDown(){ var remainSeconds = $("#remainSeconds").val(); var timeout; if(remainSeconds > 0){//秒杀还没开始,倒计时 $("#buyButton").attr("disabled", true); $("#miaoshaTip").html("秒杀倒计时:"+remainSeconds+""); timeout = setTimeout(function(){ $("#countDown").text(remainSeconds - 1); $("#remainSeconds").val(remainSeconds - 1); countDown(); },1000); }else if(remainSeconds == 0){//秒杀进行中 $("#buyButton").attr("disabled", false); if(timeout){ clearTimeout(timeout); } $("#miaoshaTip").html("秒杀进行中"); }else{//秒杀已经结束 $("#buyButton").attr("disabled", true); $("#miaoshaTip").html("秒杀已经结束"); } }

② com.kirin.miaosha.controller / MiaoshaController.java:

package com.kirin.miaosha.controller;

@Controller
@RequestMapping("/miaosha")
public class MiaoshaController {

    @Autowired
    MiaoshaUserService userService;
    
    @Autowired
    RedisService redisService;
    
    @Autowired
    GoodsService goodsService;
    
    @Autowired
    OrderService orderService;
    
    @Autowired
    MiaoshaService miaoshaService;
    
    //秒杀表单提交
    @RequestMapping(value="/do_miaosha", method=RequestMethod.POST)
    @ResponseBody
    public Result miaosha(Model model,MiaoshaUser user,
            @RequestParam("goodsId")long goodsId) {
        model.addAttribute("user", user);
        if(user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        //判断库存
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
        int stock = goods.getStockCount();
        if(stock <= 0) {
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }
        //判断是否已经秒杀到了
        MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
        if(order != null) {
            return Result.error(CodeMsg.REPEATE_MIAOSHA);
        }
        //减库存 下订单 写入秒杀订单
        OrderInfo orderInfo = miaoshaService.miaosha(user, goods);
        return Result.success(orderInfo);
    }
}

③ static / order_detail.htm:

3、订单详情静态化

① static / order_detail.htm:




    订单详情
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    


    
秒杀订单详情
商品名称
商品图片
订单价格
下单时间
订单状态
收货人 XXX 18812341234
收货地址 北京市昌平区回龙观龙博一区

② 新建 com.kirin.miaosha.controller / OrderController.java:

package com.kirin.miaosha.controller;

@Controller
@RequestMapping("/order")
public class OrderController {

    @Autowired
    MiaoshaUserService userService;
    
    @Autowired
    RedisService redisService;
    
    @Autowired
    OrderService orderService;
    
    @Autowired
    GoodsService goodsService;
    
    @RequestMapping("/detail")
    @ResponseBody
    public Result info(Model model,MiaoshaUser user,
            @RequestParam("orderId") long orderId) {
        if(user == null) { //用户为空,提示重新登录
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        OrderInfo order = orderService.getOrderById(orderId);
        if(order == null) { //订单为空,提示错误
            return Result.error(CodeMsg.ORDER_NOT_EXIST);
        }
        long goodsId = order.getGoodsId(); //获取订单中的商品id
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId); //通过商品id获取商品
        OrderDetailVo vo = new OrderDetailVo();
        vo.setOrder(order);
        vo.setGoods(goods);
        return Result.success(vo);
    }
}

③ 新建 com.kirin.miaosha.vo / OrderDetailVo.java:

package com.kirin.miaosha.vo;

import com.kirin.miaosha.domain.OrderInfo;

public class OrderDetailVo {
    private GoodsVo goods;
    private OrderInfo order;
    public GoodsVo getGoods() {
        return goods;
    }
    public void setGoods(GoodsVo goods) {
        this.goods = goods;
    }
    public OrderInfo getOrder() {
        return order;
    }
    public void setOrder(OrderInfo order) {
        this.order = order;
    }
}

④ com.kirin.miaosha.service / OrderService.java:

package com.kirin.miaosha.service;

@Service
public class OrderService {
    
    @Autowired
    OrderDao orderDao;
    
    @Autowired
    RedisService redisService;
    
    public MiaoshaOrder getMiaoshaOrderByUserIdGoodsId(long userId, long goodsId) {
        return orderDao.getMiaoshaOrderByUserIdGoodsId(userId, goodsId);
    }
    
    public OrderInfo getOrderById(long orderId) {
        return orderDao.getOrderById(orderId);
    }

    //生成订单
    @Transactional
    public OrderInfo createOrder(MiaoshaUser user, GoodsVo goods) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setCreateDate(new Date());
        orderInfo.setDeliveryAddrId(0L);
        orderInfo.setGoodsCount(1);
        orderInfo.setGoodsId(goods.getId());
        orderInfo.setGoodsName(goods.getGoodsName());
        orderInfo.setGoodsPrice(goods.getMiaoshaPrice());
        orderInfo.setOrderChannel(1);
        orderInfo.setStatus(0);
        orderInfo.setUserId(user.getId());
        long orderId = orderDao.insert(orderInfo);
        MiaoshaOrder miaoshaOrder = new MiaoshaOrder();
        miaoshaOrder.setGoodsId(goods.getId());
        miaoshaOrder.setOrderId(orderId);
        miaoshaOrder.setUserId(user.getId());
        orderDao.insertMiaoshaOrder(miaoshaOrder);
        return orderInfo;
    }
}

⑤ com.kirin.miaosha.dao / OrderDao.java:

package com.kirin.miaosha.dao;

@Mapper
public interface OrderDao {
    
    @Select("select * from miaosha_order where user_id=#{userId} and goods_id=#{goodsId}")
    public MiaoshaOrder getMiaoshaOrderByUserIdGoodsId(@Param("userId")long userId, @Param("goodsId")long goodsId);

    @Insert("insert into order_info(user_id, goods_id, goods_name, goods_count, goods_price, order_channel, status, create_date)values("
            + "#{userId}, #{goodsId}, #{goodsName}, #{goodsCount}, #{goodsPrice}, #{orderChannel},#{status},#{createDate} )")
    @SelectKey(keyColumn="id", keyProperty="id", resultType=long.class, before=false, statement="select last_insert_id()")
    public long insert(OrderInfo orderInfo);
    
    @Insert("insert into miaosha_order (user_id, goods_id, order_id)values(#{userId}, #{goodsId}, #{orderId})")
    public int insertMiaoshaOrder(MiaoshaOrder miaoshaOrder);    
    
    @Select("select * from order_info where id = #{orderId}")
    public OrderInfo getOrderById(@Param("orderId")long orderId);
}

⑥ com.kirin.miaosha.result / CodeMsg.java:

//订单模块5004XX
public static CodeMsg ORDER_NOT_EXIST = new CodeMsg(500400, "订单不存在");

4、★解决超卖问题

超卖问题:压测时,出现秒杀商品数量为 - 的情况

(1)当多个线程同时读取到同一个库存数量时,防止超卖,修改SQL语句

com.kirin.miaosha.dao / GoodsDao.java:

(2)防止同一个用户秒杀多商品,添加唯一索引,绑定user_id和goods_id,这样同一个用户对同一个商品的秒杀订单是唯一的

(3)压测,查看是否解决超卖问题

① 压测前,小优化:在判断有没有秒杀到商品时,不用查数据库,可以直接查缓存

com.kirin.miaosha.service / OrderService.java:

com.kirin.miaosha.redis / OrderKey.java:

package com.kirin.miaosha.redis;

public class OrderKey extends BasePrefix {

    public OrderKey(String prefix) {
        super(prefix);
    }
    public static OrderKey getMiaoshaOrderByUidGid = new OrderKey("moug");
}

② 压测

相关