商品詳情及靜態化
商品詳情
商品詳情瀏覽量比較大,併發高,我們會獨立開啓一個微服務,用來展示商品詳情。
服務:leyou-goods-web,其端口爲8084
nginx --port=8084
需要portal服務的9092端口提供圖片解析
-
後臺接口
-
使用 thymeleaf修改item.html
商品詳情頁服務
我們從leyou-portal中複製item.html模板到當前項目resource目錄下的templates中:
頁面跳轉
nginx反向代理
把以/item開頭的請求,代理到我們的8084端口。
編寫跳轉controller
在leyou-goods-web中編寫controller,接收請求,並跳轉到商品詳情頁:
@Controller
@RequestMapping("item")
public class GoodsController {
/**
* 跳轉到商品詳情頁
* @param model
* @param id
* @return
*/
@GetMapping("{id}.html")
public String toItemPage(Model model, @PathVariable("id")Long id){
return "item";
}
}
封裝模型數據
商品微服務提供接口
創建FeignClient
我們在leyou-goods-web服務中,創建FeignClient:
創建一個GoodsService,在裏面來封裝數據模型。
商品詳情頁(spuId.html)
分成上下兩部分:
- 上部:展示的是規格屬性列表
- 下部:展示的是商品詳情
商品詳情是HTML代碼,我們不能使用 th:text
,應該使用th:utext
在頁面的第444行左右:
<!--商品詳情-->
<div class="intro-detail" th:utext="${spuDetail.description}">
</div>
頁面靜態化
頁面靜態化
靜態化是指把動態生成的HTML頁面變爲靜態內容保存,以後用戶的請求到來,直接訪問靜態頁面,不再經過服務的渲染。
而靜態的HTML頁面可以部署在nginx中,從而大大提高併發能力,減小tomcat壓力。
Thymeleaf除了可以把渲染結果寫入Response,也可以寫到本地文件,從而實現靜態化。
Thymeleaf實現靜態化(存在磁盤上)
- nginx代理靜態頁面
<div th:text-"${message}">數據詳情</div>
1.商品詳情頁
thymeleaf語法
1.引入thymeleaf啓動器
2.關閉thymeleaf緩存:spring.thymeleaf.cache=false ctrl+shift+f9
3.th:text th:utext th:each ${} /*[[${數據模型}]]*/
4.頁面數據的組織
2.頁面靜態化
context:thymeleaf的運行上下文,存放數據模型
TemplateResolver:模板解析器,模板的位置,名稱,後綴信息
TemplateEngine:模板解析引擎
templateEngine.process("item", context, printWriter)
@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);
}
}
什麼時候創建靜態文件?
假如大部分的商品都有了靜態頁面。那麼用戶的請求都會被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 的代碼不能對用戶請求產生影響,所以這裏我們使用額外的線程進行異步創建。
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;
}
}