1. 程式人生 > >樂優商城(二十三)——商品詳情及靜態化

樂優商城(二十三)——商品詳情及靜態化

目錄

2.1 簡介

二、頁面靜態化

2.1 簡介

2.1.1 問題分析

現在的頁面是通過Thymeleaf模板引擎渲染後返回到客戶端。在後臺需要大量的資料查詢,而後渲染得到HTML頁面。會對資料庫造成壓力,並且請求的響應時間過長,併發能力不高。

首先能想到的就是快取技術,比如之前學習過的Redis。不過Redis適合資料規模比較小的情況。假如資料量比較大,例如商品詳情頁。每個頁面如果10kb,100萬商品,就是10GB空間,對記憶體佔用比較大。此時就給快取系統帶來極大壓力,如果快取崩潰,接下來倒黴的就是資料庫了。

所以快取並不是萬能的,某些場景需要其它技術來解決,比如靜態化。

2.1.2 什麼是靜態化

靜態化是指把動態生成的HTML頁面變為靜態內容儲存,以後使用者的請求到來,直接訪問靜態頁面,不再經過服務的渲染。

而靜態的HTML頁面可以部署在nginx中,從而大大提高併發能力,減小tomcat壓力。

2.1.3 如何實現靜態化

目前,靜態化頁面都是通過模板引擎來生成,而後儲存到nginx伺服器來部署。常用的模板引擎比如:

  • Freemarker

  • Velocity

  • Thymeleaf

因為之前就使用的Thymeleaf,來渲染html返回給使用者。Thymeleaf除了可以把渲染結果寫入Response,也可以寫到本地檔案,從而實現靜態化。

2.2 Thymeleaf實現靜態化

2.2.1 概念

介紹一下Thymeleaf中的幾個概念:

  • Context:執行上下文

  • TemplateResolver:模板解析器

  • TemplateEngine:模板引擎

Context

上下文: 用來儲存模型資料,當模板引擎渲染時,可以從Context上下文中獲取資料用於渲染。

當與SpringBoot結合使用時,我們放入Model的資料就會被處理到Context,作為模板渲染的資料使用。

TemplateResolver

模板解析器:用來讀取模板相關的配置,例如:模板存放的位置資訊,模板檔名稱,模板檔案的型別等等。

當與SpringBoot結合時,TemplateResolver已經由其建立完成,並且各種配置也都有預設值,比如模板存放位置,其預設值就是:templates。比如模板檔案型別,其預設值就是html。

TemplateEngine

模板引擎:用來解析模板的引擎,需要使用到上下文、模板解析器。分別從兩者中獲取模板中需要的資料,模板檔案。然後利用內建的語法規則解析,從而輸出解析後的檔案。模板引擎進行處理的函式:

templateEngine.process("模板名", context, writer);

三個引數:

  • 模板名稱

  • 上下文:裡面包含模型資料

  • writer:輸出目的地的流

在輸出時,我們可以指定輸出的目的地,如果目的地是Response的流,那就是網路響應。如果目的地是本地檔案,那就實現靜態化了。

而在SpringBoot中已經自動配置了模板引擎,因此不需要關心這個。現在就做靜態化,就是把輸出的目的地改成本地檔案即可!

2.2.2 具體實現

GoodsHtmlService

package com.leyou.service;

/**
 * @Author: 98050
 * @Time: 2018-10-19 09:40
 * @Feature: 頁面詳情靜態化介面
 */
public interface GoodsHtmlService {

    /**
     * 建立html頁面
     * @param spuId
     */
    void createHtml(Long spuId);

    /**
     * 新建執行緒處理頁面靜態化
     * @param spuId
     */
    void asyncExecute(Long spuId);

}

GoodsHtmlServiceImpl

package com.leyou.service.impl;

import com.leyou.service.GoodsHtmlService;
import com.leyou.service.GoodsService;


import com.leyou.utils.ThreadUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.Map;

/**
 * @Author: 98050
 * @Time: 2018-10-19 09:46
 * @Feature: 實現頁面靜態化介面
 */
@Service
public class GoodsHtmlServiceImpl implements GoodsHtmlService {

    @Autowired
    private GoodsService goodsService;

    @Autowired
    private TemplateEngine templateEngine;

    private static final Logger LOGGER = LoggerFactory.getLogger(GoodsHtmlService.class);


    @Override
    public void createHtml(Long spuId) {
        PrintWriter writer = null;

        //獲取頁面資料
        Map<String,Object> spuMap = this.goodsService.loadModel(spuId);
        //建立Thymeleaf上下文物件
        Context context = new Context();
        //把資料放入上下文物件
        context.setVariables(spuMap);

        //建立輸出流
        File file = new File("D:\\nginx-1.12.2\\html\\item"+spuId+".html");
        try {
            writer = new PrintWriter(file);

            //執行頁面靜態化方法
            templateEngine.process("item",context,writer);
        } catch (FileNotFoundException e) {
            LOGGER.error("頁面靜態化出錯:{}"+e,spuId);
        }finally {
            if (writer != null){
                writer.close();
            }
        }
    }

    /**
     * 新建執行緒處理頁面靜態化
     * @param spuId
     */
    @Override
    public void asyncExecute(Long spuId) {
        ThreadUtils.execute(() ->createHtml(spuId));
    }
}

ThreadUtils 

package com.leyou.utils;

import java.util.concurrent.*;

/**
 * @Author: 98050
 * @Time: 2018-10-19 10:19
 * @Feature: 執行緒工具
 */
public class ThreadUtils {

    private static final ExecutorService es = Executors.newFixedThreadPool(10);

    public static void execute(Runnable runnable){
        es.submit(runnable);
    }
}

2.2.3 什麼時候建立靜態檔案

編寫好了建立靜態檔案的service,那麼問題來了:什麼時候去呼叫它呢

場景描述:

假如大部分的商品都有了靜態頁面。那麼使用者的請求都會被nginx攔截下來,根本不會到達我們的leyou-goods-web服務。只有那些還沒有頁面的請求,才可能會到達這裡。

因此,如果請求到達了這裡,除了返回頁面檢視外,還應該建立一個靜態頁面,那麼下次就不會再來訪問leyou-goods-web服務了。

所以,在GoodsController中新增邏輯,去生成靜態html檔案:

注意:生成html 的程式碼不能對使用者請求產生影響,所以這裡使用額外的執行緒進行非同步建立。

2.2.4 重啟測試

訪問一個商品詳情,然後檢視nginx目錄:

2.3 nginx代理靜態頁面

修改nginx,讓它對商品請求進行監聽,指向本地靜態頁面,如果本地沒找到,才進行反向代理:

注意if後又空格。

重啟一下nginx

對比:

原來的載入時間

 第二次重新整理頁面的載入時間

快了很多。