秒殺系統Web實踐——05頁面優化(頁面緩存+url緩存+對象緩存、頁面靜態化+前後端分離、靜態資源優化、CDN優化)

第五章 頁面優化技術

1.頁面緩存

1.1頁面緩存(商品列表頁面)

1.2 URL緩存(商品詳情)

1.3 對象緩存

2.頁面靜態化

2.1前後端分離

2.2瀏覽器緩存

3.靜態資源優化

綜述


1.頁面緩存

我們在優化之前,接口返回的大多是頁面名稱,或響應數據,在這裏我們對頁面進行緩存處理,接口將返回靜態頁面的代碼。

步驟:1 取緩存;2 手動渲染模板;3 輸出結果

經過取緩存、手動渲染模板、輸出結果三個步驟完善商品列表頁、商品詳細。

1.1頁面緩存(商品列表頁面)

    @RequestMapping(value="/to_list", produces="text/html")
    @ResponseBody
    public String list(Model model, MiaoshaUser user, HttpServletRequest request, HttpServletResponse response){
//        System.out.println(user);
        model.addAttribute("user",user);
        //取緩存
        String html = redisService.get(GoodsKey.getGoodsList, "", String.class);
        if(!StringUtils.isEmpty(html)) {
            return html;
        }
        //取數據庫
        List<GoodsVo> goodsList = goodsService.listGoodsVo();
        model.addAttribute("goodsList",goodsList);
        //return "goods_list";

        //手動渲染
        //https://blog.csdn.net/ouzhuangzhuang/article/details/84839266
        IWebContext ctx =new WebContext(request,response, request.getServletContext(),request.getLocale(),model.asMap());
        html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);
        if(!StringUtils.isEmpty(html)) {
            redisService.set(GoodsKey.getGoodsList, "", html);
        }
        return html;
    }


public class GoodsKey extends BasePrefix{
    private GoodsKey(int expireSeconds, String prefix) {
        super(expireSeconds, prefix);
    }
    public static GoodsKey getGoodsList = new GoodsKey(60, "gl");
    public static GoodsKey getGoodsDetail = new GoodsKey(60, "gd");
}

1.2 URL緩存(商品詳情)

    @RequestMapping(value="/to_detail2/{goodsId}",produces="text/html")
    @ResponseBody
    public String detai2(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user,
                         @PathVariable("goodsId")long goodsId){


        model.addAttribute("user",user);


        //取緩存
        String html = redisService.get(GoodsKey.getGoodsDetail, ""+goodsId, String.class);
        if(!StringUtils.isEmpty(html)) {
            return html;
        }

        //手動渲染
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
        //System.out.println(goods.toString());
        model.addAttribute("goods",goods);
        
        //
        long startAt = goods.getStartDate().getTime();
        long endAt = goods.getEndDate().getTime();
        long now = System.currentTimeMillis();

        int miaoshaStatus = 0;
        int remainSeconds = 0;
        if(now < startAt ) {//秒殺還沒開始,倒計時
            miaoshaStatus = 0;
            remainSeconds = (int)((startAt - now )/1000);
        }else  if(now > endAt){//秒殺已經結束
            miaoshaStatus = 2;
            remainSeconds = -1;
        }else {//秒殺進行中
            miaoshaStatus = 1;
            remainSeconds = 0;
        }
        //秒殺狀態
        model.addAttribute("miaoshaStatus", miaoshaStatus);
        //秒殺倒計時
        model.addAttribute("remainSeconds", remainSeconds);
        //return "goods_detail";

        //手動渲染
        IWebContext ctx =new WebContext(request,response,
                request.getServletContext(),request.getLocale(),model.asMap());
        html = thymeleafViewResolver.getTemplateEngine().process("goods_detail", ctx);
        if(!StringUtils.isEmpty(html)) {
            redisService.set(GoodsKey.getGoodsDetail, ""+goodsId, html);
        }
        return html;
    }

1.3 對象緩存

使用對象緩存重寫MaioshaService裏面的getById方法,使得不直接訪問數據庫,同時也要重寫updatePassword方法。

    public MiaoshaUser getById(long id){
        //取緩存
        MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, ""+id, MiaoshaUser.class);
        if(user != null) {
            return user;
        }
        //取數據庫
        user = miaoshaUserDao.getById(id);
        //加緩存
        if(user != null) {
            redisService.set(MiaoshaUserKey.getById, ""+id, user);
        }
        return user;
    }

    public boolean updatePassword(String token, long id, String formPass) {
        //取user
        MiaoshaUser user = getById(id);
        if(user == null) {
            throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
        }
        //更新數據庫
        MiaoshaUser toBeUpdate = new MiaoshaUser();
        toBeUpdate.setId(id);
        toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPass, user.getSalt()));
        miaoshaUserDao.update(toBeUpdate);
        //處理緩存
        redisService.delete(MiaoshaUserKey.getById, ""+id);
        user.setPassword(toBeUpdate.getPassword());
        redisService.set(MiaoshaUserKey.token, token, user);
        return true;
    }

2.頁面靜態化

2.1前後端分離

頁面優化使用前後端分離,具體的在該項目中在商品列表跳轉到商品詳情可以體現,在靜態頁面初始化的時候,調用後端接口,獲取商品詳情,實現頁面靜態化

  @RequestMapping(value="/detail/{goodsId}")
    @ResponseBody
    public Result<GoodsDetailVo> detail(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user,
                                        @PathVariable("goodsId")long goodsId) {
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
        long startAt = goods.getStartDate().getTime();
        long endAt = goods.getEndDate().getTime();
        long now = System.currentTimeMillis();
        int miaoshaStatus = 0;
        int remainSeconds = 0;
        if(now < startAt ) {//秒殺還沒開始,倒計時
            miaoshaStatus = 0;
            remainSeconds = (int)((startAt - now )/1000);
        }else  if(now > endAt){//秒殺已經結束
            miaoshaStatus = 2;
            remainSeconds = -1;
        }else {//秒殺進行中
            miaoshaStatus = 1;
            remainSeconds = 0;
        }
        GoodsDetailVo vo = new GoodsDetailVo();
        vo.setGoods(goods);
        vo.setUser(user);
        vo.setRemainSeconds(remainSeconds);
        vo.setMiaoshaStatus(miaoshaStatus);
        return Result.success(vo);
    }
//列表頁
<td><a th:href="'/goods_detail.htm?goodsId='+${goods.id}">詳情</a></td>



//goods_detail.htm
<!DOCTYPE HTML>
<html >
<head>
    <title>商品詳情</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <!-- jquery -->
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    <!-- bootstrap -->
    <link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css" />
    <script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
    <!-- jquery-validator -->
    <script type="text/javascript" src="/jquery-validation/jquery.validate.min.js"></script>
    <script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js"></script>
    <!-- layer -->
    <script type="text/javascript" src="/layer/layer.js"></script>
    <!-- md5.js -->
    <script type="text/javascript" src="/js/md5.min.js"></script>
    <!-- common.js -->
    <script type="text/javascript" src="/js/common.js"></script>
    <style type="text/css">
        html,body{
            height:100%;
            width:100%;
        }
        body{
            background:url('/img/bg2.jpg') no-repeat;
            background-size:100% 100%;
        }
        #goodslist td{
            border-top:1px solid #39503f61;
        }
    </style>
</head>
<body>

<div class="panel panel-default" style="height:100%;background-color:rgba(222,222,222,0.8)" >
  <div class="panel-heading">秒殺商品詳情</div>
  <div class="panel-body">
  	<span id="userTip"> 您還沒有登錄,請登陸後再操作<br/></span>
  	<span>沒有收貨地址的提示。。。</span>
  </div>
  <table class="table" id="goodslist">
  	<tr>  
        <td>商品名稱</td>  
        <td colspan="3" id="goodsName"></td> 
     </tr>  
     <tr>  
        <td>商品圖片</td>  
        <td colspan="3"><img  id="goodsImg" width="200" height="200" /></td>  
     </tr>
     <tr>  
        <td>秒殺開始時間</td>  
        <td id="startTime"></td>
        <td >	
        	<input type="hidden" id="remainSeconds" />
        	<span id="miaoshaTip"></span>
        </td>
        <td>
            <button class="btn btn-primary" type="button" id="buyButton" onclick="doMiaosha()">立即秒殺</button>
        	<input type="hidden" name="goodsId"  id="goodsId" />
        </td>
     </tr>
     <tr>  
        <td>商品原價</td>  
        <td colspan="3" id="goodsPrice"></td>  
     </tr>
      <tr>  
        <td>秒殺價</td>  
        <td colspan="3"  id="miaoshaPrice"></td>  
     </tr>
     <tr>  
        <td>庫存數量</td>  
        <td colspan="3"  id="stockCount"></td>  
     </tr>
  </table>
</div>
</body>
<script>

function doMiaosha(){
    $.ajax({
    url:"/miaosha/do_miaosha",
    type:"POST",
        data:{
            goodsId:$("#goodsId").val(),
        },
        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("客戶端請求有誤");
        }
    });
}

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

function getDetail(){
	var goodsId = g_getQueryString("goodsId");
	$.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("秒殺進行中");
		$("#verifyCodeImg").attr("src", "/miaosha/verifyCode?goodsId="+$("#goodsId").val());
		$("#verifyCodeImg").show();
		$("#verifyCode").show();
	}else{//秒殺已經結束
		$("#buyButton").attr("disabled", true);
		$("#miaoshaTip").html("秒殺已經結束");
		$("#verifyCodeImg").hide();
		$("#verifyCode").hide();
	}
}
function refreshVerifyCode(){
	$("#verifyCodeImg").attr("src", "/miaosha/verifyCode?goodsId="+$("#goodsId").val()+"&timestamp="+new Date().getTime());
}
</script>
</html>

2.2瀏覽器緩存

添加配置

#static
#啓用靜態資源處理
spring.resources.add-mappings=true
#客戶端緩存時間
spring.resources.cache.period=3600
spring.resources.chain.cache=true 
spring.resources.chain.enabled=true
#啓用html5的application-cache
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/

3.靜態資源優化

下面我們來了一下靜態資源優化常用的手段

1 JS/CSS壓縮,減少流量

2 多個JS/CSS組合,減少連接數

3 CDN就近訪問

工具有

Tengine:http://tengine.taobao.org/

webback

CDN


綜述

優化完成後,我們可以再次用JMeter進行性能測試,看看QPS是否變大了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章