商城-商品詳情及靜态化-頁面靜态化
- 2.頁面靜态化
-
- 2.1.簡介
-
- 2.1.1.問題分析
- 2.1.2.什麼是靜态化
- 2.1.3.如何實作靜态化
- 2.2.Thymeleaf實作靜态化
-
- 2.2.1.概念
- 2.2.2.具體實作
- 2.2.3.什麼時候建立靜态檔案
- 2.2.4.重新開機測試:
- 2.3.nginx代理靜态頁面
2.頁面靜态化
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.具體實作
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSP9cHZ6B3MYVnVHFmN1clWv50MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4gzMwMDOyEjM3EjNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
Service代碼:
@Service
public class GoodsHtmlService {
@Autowired
private GoodsService goodsService;
@Autowired
private TemplateEngine templateEngine;
private static final Logger LOGGER = LoggerFactory.getLogger(GoodsHtmlService.class);
/**
* 建立html頁面
*
* @param spuId
* @throws Exception
*/
public void createHtml(Long spuId) {
PrintWriter writer = null;
try {
// 擷取頁面資料
Map<String, Object> spuMap = this.goodsService.loadModel(spuId);
// 建立thymeleaf上下文對象
Context context = new Context();
// 把資料放入上下文對象
context.setVariables(spuMap);
// 建立輸出流
File file = new File("C:\\project\\nginx-1.14.0\\html\\item\\" + spuId + ".html");
writer = new PrintWriter(file);
// 執行頁面靜态化方法
templateEngine.process("item", context, writer);
} catch (Exception e) {
LOGGER.error("頁面靜态化出錯:{},"+ e, spuId);
} finally {
if (writer != null) {
writer.close();
}
}
}
/**
* 建立線程處理頁面靜态化
* @param spuId
*/
public void asyncExcute(Long spuId) {
ThreadUtils.execute(()->createHtml(spuId));
/*ThreadUtils.execute(new Runnable() {
@Override
public void run() {
createHtml(spuId);
}
});*/
}
}
線程工具類:
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
服務。隻有那些還沒有頁面的請求,才可能會到達這裡。
是以,如果請求到達了這裡,我們除了傳回頁面視圖外,還應該建立一個靜态頁面,那麼下次就不會再來麻煩我們了。
是以,我們在GoodsController中添加邏輯,去生成靜态html檔案:
@GetMapping("{id}.html")
public String toItemPage(@PathVariable("id")Long id, Model model){
// 加載所需的資料
Map<String, Object> map = this.goodsService.loadModel(id);
// 把資料放入資料模型
model.addAllAttributes(map);
// 頁面靜态化
this.goodsHtmlService.asyncExcute(id);
return "item";
}
注意:生成html 的代碼不能對使用者請求産生影響,是以這裡我們使用額外的線程進行異步建立。
2.2.4.重新開機測試:
通路一個商品詳情,然後檢視nginx目錄:
2.3.nginx代理靜态頁面
接下來,我們修改nginx,讓它對商品請求進行監聽,指向本地靜态頁面,如果本地沒找到,才進行反向代理:
server {
listen 80;
server_name www.leyou.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location /item {
# 先找本地
root html;
if (!-f $request_filename) { #請求的檔案不存在,就反向代理
proxy_pass http://127.0.0.1:8084;
break;
}
}
location / {
proxy_pass http://127.0.0.1:9002;
proxy_connect_timeout 600;
proxy_read_timeout 600;
}
}
重新開機測試:
發現請求速度得到了極大提升: