SpringBoot整合Thymeleaf的方法
1. 問題需求分析
- 在做樂優商城時,頁面是通過Thymeleaf模板引擎渲染後返回到客戶端。當商品詳情頁數據渲染時,在後臺需要大量的數據查詢,而後渲染得到HTML頁面。在用戶訪問量大的情況下會對數據庫造成壓力,並且請求的響應時間過長,並發能力不高。
如何解決?
- 一般我們優先會考慮使用緩存技術,比如 Redis 分佈式緩存,Guava 本地緩存等。然而 Redis 隻適合數據規模比較小的情況,假如數據量比較大,例如商品詳情頁,每個頁面如果10kb,100萬商品,就是10GB空間,對內存占用比較大。此時就給緩存系統帶來極大壓力,如果緩存崩潰,接下來倒黴的就是數據庫瞭。
- 所以緩存並不是萬能的,某些場景需要其它技術來解決,比如靜態化技術。
2. 什麼是靜態化?
- 靜態化是指把動態生成的HTML頁面變為靜態內容保存,以後用戶的請求到來,直接訪問靜態頁面,不再經過服務的渲染。
- 而靜態的HTML頁面可以部署在nginx中,從而大大提高並發能力,減小tomcat壓力。
3. 如何實現靜態化?
目前,靜態化頁面都是通過模板引擎來生成,而後保存到nginx服務器來部署。常用的模板引擎比如:
- Freemarker
- Velocity
- Thymeleaf
樂優項目中使用的是Thymeleaf 模板引擎!
4. Thymeleaf實現靜態化
4.1 概念介紹
Thymeleaf 模板引擎實現頁面靜態化的幾個概念介紹:
- Context:運行上下文
- TemplateResolver:模板解析器
- TemplateEngine:模板引擎
Context:
Thymeleaf 模板中的上下文: 用來保存模型數據,當模板引擎渲染時,可以從Context上下文中獲取數據用於渲染。
當與Spring Boot結合使用時,我們放入Model的數據就會被處理到Context,作為模板渲染的數據使用。
TemplateResolver:
Thymeleaf 模板中的模板解析器:用來讀取模板相關的配置,例如:模板存放的位置信息,模板文件名稱,模板文件的類型等等。
當與SpringBoot結合時,TemplateResolver已經由其創建完成,並且各種配置也都有默認值,比如模板存放位置,其默認值就是:templates。比如模板文件類型,其默認值就是html。
TemplateEngine:
Thymeleaf 模板中的模板引擎:用來解析模板的引擎,需要使用到上下文、模板解析器。分別從兩者中獲取模板中需要的數據,模板文件。然後利用內置的語法規則解析,從而輸出解析後的文件。來看下模板引擎進行處理的函數:
templateEngine.process("模板名", context, writer);
三個參數:
- 模板名稱
- 上下文:裡面包含模型數據
- writer:輸出目的地的流
在輸出時,我們可以指定輸出的目的地,如果目的地是Response的流,那就是網絡響應。如果目的地是本地文件,那就實現靜態化瞭。
而在SpringBoot中已經自動配置瞭模板引擎,因此不需要關心這個。我們做靜態化,就是把輸出的目的地改成本地文件即可!
4.2 具體實現
GoodsHtmlService
/** * @Auther: csp1999 * @Date: 2020/12/16/19:43 * @Description: 生成HTML頁面的Service */ @Service public class GoodsHtmlService { // themleaf 模板引擎 @Autowired private TemplateEngine templateEngine; @Autowired private GoodsService goodsService; /** * 根據spuId 將對應的商品詳情頁面生成HTML靜態頁面 * * @param spuId */ public void createHTML(Long spuId) { // 初始化運行上下文: org.thymeleaf.context 包下 Context context = new Context(); // 設置數據模板 // 從goodsService.loadData(spuId) 方法中獲取模板頁面中需要渲染的數據 context.setVariables(goodsService.loadData(spuId)); // 獲取輸出流 // 將需要輸出的html 文件地址設置為服務器中的nginx 目錄下的html 目錄中的item文件夾下(我這裡nginx 部署的是win10本地!) File file = new File( "M:\\note\\MyProjects\\leyou\\tools\\nginx-1.14.0\\html\\item\\" + spuId + ".html"); PrintWriter writer = null; try { writer = new PrintWriter(file); // 模板引擎生成靜態html 頁面 templateEngine.process("item", context, writer); } catch (FileNotFoundException e) { e.printStackTrace(); } 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); } });*/ } }
多線程工具類ThreadUtils
/** * @Auther: csp1999 * @Date: 2020/12/16/20:18 * @Description: 多線程工具類 */ public class ThreadUtils { private static final ExecutorService es = Executors.newFixedThreadPool(10); public static void execute(Runnable runnable) { es.submit(runnable); } }
GoodsController
在調用GoodsController訪問商品頁面的同時生成緩存的頁面:
/** * @Auther: csp1999 * @Date: 2020/12/16/10:00 * @Description: */ @Controller public class GoodsController { @Autowired private GoodsService goodsService; @Autowired private GoodsHtmlService goodsHtmlService; /** * 商品詳情也頁面 * * @param id * @param model * @return */ @GetMapping("/item/{id}.html") public String toItemPage(@PathVariable("id") Long id, Model model) { Map<String, Object> map = goodsService.loadData(id); model.addAllAttributes(map); // 頁面靜態化:調用createHTML(id) 方法生成靜態頁面並保存到服務器的nginx 對應目錄下緩存 // 通過多線程方式: goodsHtmlService.asyncExcute(id); // goodsHtmlService.createHTML(id); return "/item"; } }
註意:生成html 的代碼不能對用戶請求產生影響,所以這裡我們使用額外的線程進行異步創建。
5. 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; # 如果是以item 開頭的路徑,代理到前臺8084端口 # eg: www.leyou.com/item/4586.html location /item { # 先到本地nginx 路徑下的html文件夾 中找頁面,如果有則走該頁面(相當於緩存效果) root html; if (!-f $request_filename) { # 如果請求的文件不存在,就反向代理,通過請求去訪問該頁面 proxy_pass http://127.0.0.1:8084; break; } } # 代理到前臺9002端口 location / { proxy_pass http://127.0.0.1:9002; proxy_connect_timeout 600; proxy_read_timeout 600; } }
nginx 代理的流程圖:
6. 訪問頁面測試
第一次訪問頁面:
nginx路徑下的html/item/下生成瞭該頁面的靜態資源:
第二次訪問該頁面:
總結
本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!
推薦閱讀:
- 解決Springboot項目打包後的頁面丟失問題(thymeleaf報錯)
- SpringBoot QQ郵箱發送郵件實例代碼
- Springboot整合Freemarker的實現詳細過程
- 如何用Spring發送電子郵件
- SpringBoot實現定時發送郵件的三種方法案例詳解