頁面靜態化,相比不少人都聽過,可是咱們爲何使用頁面靜態化和使用頁面靜態化的好處又有哪些呢?今天這篇文章咱們就來詳細的講講。html
靜態化就是指把本來動態生成的html頁面變爲靜態內容保存,用戶客戶端請求的時候,直接返回靜態頁面,不用再通過服務渲染,不用訪問數據庫,能夠大大的減少數據庫的壓力,顯著的提升性能。nginx
在使用頁面靜態化以前,咱們的頁面都是經過動態渲染後返回給客戶端的,在後臺須要大量的查詢,然後渲染獲得html頁面,對數據庫的壓力比較大,而且請求的響應時間也比較長,併發能力並不高。一般咱們解決這樣的問題都是緩存技術,把熱點數據放入緩存中,可是也不是什麼數據都要放入緩存的,好比商品的詳情數據,若是商品不少,數據量很大的時候,佔用的內存就比較大,此時會給緩存到來很大的壓力。若是由於這個致使緩存崩潰,那麼就可能直接致使數據庫崩潰。數據庫
緩存不是萬能的,因此咱們在一些場景下就選擇使用靜態化來解決,好比,商品詳情頁、網站首頁、新聞詳情這些頁面,咱們就能夠經過靜態化,將靜態化的頁面保存在nginx服務器來部署。瀏覽器
實現靜態化頁面一般都是經過模板引擎來生成的,經常使用的模板引擎有:緩存
Freemarker
優勢:服務器
缺點:網絡
Velocity
優勢:併發
缺點:mvc
Thymeleaf
優勢:app
靜態html嵌入標籤屬性,瀏覽器能夠直接打開模板,便於聯調,是SpringBoot官方推薦方案。
缺點:
模板必須符合xml規範。
Thymeleaf除了能夠把渲染結果寫入Response,也能夠寫到本地文件實現靜態化,先來看看Thymeleaf中的幾個概念:
Context:運行上下文 TemplateResolver:模板解析器 TemplateEngine:模板引擎
Context
上下文:用來保存模型數據,當模板引擎渲染時,能夠從Context上下文獲取數據用於渲染,當與SpringBoot結合使用時,咱們放入Model的數據會被處理到Context,做爲模板渲染的數據使用。
TemplateResolver
模板解析器:用來讀取模板相關的配置,如:模板存放的位置信息、模板文件名稱、模板文件的類型等。與SpringBoot結合使用時,TemplateResolver已由其建立完成,而且各類配置也都有默認值,好比模板存放位置,默認就是:templates,模板文件類型默認就是html
TemplateEngine
模板引擎:用來解析模板的引擎,須要使用到上下文、模板解析器。分別從二者中獲取模板中須要的數據,模板文件。而後利用內置的語法規則解析,從而輸出解析後的文件。來看下模板引發進行處理的函數
templateEngine.process("模板名", context, writer);
三個參數:
第一次咱們能夠經過腳本輪詢調用每個商品的詳情頁,此時由於尚未生成靜態頁面,會調用商品服務,生成各個商品的詳情頁靜態頁面,並向相應的應用層nginx,下次請求的時候,就不用再調用商品服務。當咱們修改商品的詳情信息後,會手動刪除相應的靜態頁面,並觸發從新生成新的靜態頁面。
application.yml配置生成靜態文件保存目錄:
guli: thymeleaf: destPath: E:/GuLi/html/item
service實現:
@Service public class FileService { @Autowired private GoodsService goodsService; @Autowired private TemplateEngine templateEngine; @Value("${ly.thymeleaf.destPath}") private String destPath; /** * 建立html頁面 * @param id * @throws Exception */ public void createHtml(Long id) throws Exception { // 建立上下文, Context context = new Context(); // 把數據加入上下文 context.setVariables(this.goodsService.loadModel(id)); // 建立輸出流,關聯到一個臨時文件 File temp = new File(id + ".html"); // 目標頁面文件 File dest = createPath(id); // 備份原頁面文件 File bak = new File(id + "_bak.html"); try (PrintWriter writer = new PrintWriter(temp, "UTF-8")) { // 利用thymeleaf模板引擎生成 靜態頁面 templateEngine.process("item", context, writer); if (dest.exists()) { // 若是目標文件已經存在,先備份 dest.renameTo(bak); } // 將新頁面覆蓋舊頁面 FileCopyUtils.copy(temp,dest); // 成功後將備份頁面刪除 bak.delete(); } catch (IOException e) { // 失敗後,將備份頁面恢復 bak.renameTo(dest); // 從新拋出異常,聲明頁面生成失敗 throw new Exception(e); } finally { // 刪除臨時頁面 if (temp.exists()) { temp.delete(); } } } private File createPath(Long id) { if (id == null) { return null; } File dest = new File(this.destPath); if (!dest.exists()) { dest.mkdirs(); } return new File(dest, id + ".html"); } /** * 判斷某個商品的頁面是否存在 * @param id * @return */ public boolean exists(Long id){ return this.createPath(id).exists(); } /** * 異步建立html頁面 * @param id */ public void syncCreateHtml(Long id){ ThreadUtils.execute(() -> { try { createHtml(id); } catch (Exception e) { e.printStackTrace(); } }); } }
線程工具類:
public class ThreadUtils { private static final ExecutorService es = Executors.newFixedThreadPool(10); public static void execute(Runnable runnable) { es.submit(runnable); } }
閒聊:天氣愈來愈冷,你們能夠加入個人:Java新手學習交流,拉你進技術交流羣,一塊兒抱團取暖吧~