1. 學習計划
1、商品詳情頁面展示,動態展示 jsp + redis
2、使用freemarker實現網頁靜態化
3、ActiveMq同步生成靜態網頁
兩個方案對比,方案一依賴web容器,redis的引入確實是減輕了數據庫的壓力,卻也有明顯的不足,撇開緩存的上限不說,方案一中web容器還是會去編輯和解析jsp頁面,從緩存中拿數據,生成html返回給客戶端;而方案二完全脫離web容器,不僅減輕了數據庫的壓力,也減輕了web容器的壓力,性能更加優越。
下面分別演示這兩種解決方案。這里對搭建工程進行了精簡,主要是關注兩種方案的網頁靜態化操作。
2. jsp + redis解決方案
使用redis做緩存,向業務邏輯中添加緩存,減輕數據庫的訪問壓力,可以在一定程度上提高系統的吞吐量。
2.1. 向業務邏輯中添加緩存
2.1.1. 緩存添加分析
使用redis做緩存。
業務邏輯:
1、根據商品id到緩存中命中
2、查到緩存,直接返回。
3、差不到,查詢數據庫
4、把數據放到緩存中
5、返回數據
緩存中緩存熱點數據,提供緩存的使用率。需要設置緩存的有效期。一般是一天的時間,可以根據實際情況跳轉。
2.1.2 前綴分割
需要使用String類型來保存商品數據。
可以加前綴方法對象redis中的key進行歸類。
ITEM_INFO:123456:BASE
ITEM_INFO:123456:DESC
如果把二維表保存到redis中:
1、表名就是第一層
2、主鍵是第二層
3、字段名第三次
三層使用“:”分隔作為key,value就是字段中的內容。
2.1.3. 添加緩存
商品添加緩存
@Override public TbItem getItemById(long itemId) { try { //查詢緩存 String json = jedisClient.get(ITEM_INFO_PRE + ":" + itemId + ":BASE"); if (StringUtils.isNotBlank(json)) { //把json轉換為java對象 TbItem item = JsonUtils.jsonToPojo(json, TbItem.class); return item; } } catch (Exception e) { e.printStackTrace(); } //根據商品id查詢商品信息 //TbItem tbItem = itemMapper.selectByPrimaryKey(itemId); TbItemExample example = new TbItemExample(); //設置查詢條件 Criteria criteria = example.createCriteria(); criteria.andIdEqualTo(itemId); List<TbItem> list = itemMapper.selectByExample(example); if (list != null && list.size() > 0) { TbItem item = list.get(0); try { //把數據保存到緩存 jedisClient.set(ITEM_INFO_PRE + ":" + itemId + ":BASE", JsonUtils.objectToJson(item)); //設置緩存的有效期 jedisClient.expire(ITEM_INFO_PRE + ":" + itemId + ":BASE", ITEM_INFO_EXPIRE); } catch (Exception e) { e.printStackTrace(); } return item; } return null; }
取商品描述添加緩存:
@Override public TbItemDesc getItemDescById(long itemId) { try { String json = jedisClient.get(ITEM_INFO_PRE + ":" + itemId + ":DESC"); //判斷緩存是否命中 if (StringUtils.isNotBlank(json) ) { //轉換為java對象 TbItemDesc itemDesc = JsonUtils.jsonToPojo(json, TbItemDesc.class); return itemDesc; } } catch (Exception e) { e.printStackTrace(); } TbItemDesc itemDesc = itemDescMapper.selectByPrimaryKey(itemId); try { jedisClient.set(ITEM_INFO_PRE + ":" + itemId + ":DESC", JsonUtils.objectToJson(itemDesc)); //設置過期時間 jedisClient.expire(ITEM_INFO_PRE + ":" + itemId + ":DESC", ITEM_INFO_EXPIRE); } catch (Exception e) { e.printStackTrace(); } return itemDesc; }
3. 網頁靜態化
可以使用Freemarker實現網頁靜態化。
3.0. Freemarker的jar包
把freemarker的jar包添加到工程中。
Maven工程添加依賴
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency>
3.1. 什么是freemarker
FreeMarker是一個用Java語言編寫的模板引擎,它基於模板來生成文本輸出。FreeMarker與Web容器無關,即在Web運行時,它並不知道Servlet或HTTP。它不僅可以用作表現層的實現技術,而且還可以用於生成XML,JSP或Java 等。
目前企業中:主要用Freemarker做靜態頁面或是頁面展示
3.1.1. 原理:
3.1.2. 模板:
任意一個文本文件,都可以作為模板,模板文件的擴展名一般是ftl,freemarker根據提供的模板,將模板中對應的標記(可以看做key),填充為相應的數據,freemarker不僅僅可以生成靜態的html文件,只要給定合適的模板個數據可以生成任何的文本文件。
3.2. freemarker使用步驟:
第一步:創建一個Configuration對象,直接new一個對象。構造方法的參數就是freemarker對於的版本號。
第二步:設置模板文件所在的路徑。
第三步:設置模板文件使用的字符集。一般就是utf-8.
第四步:加載一個模板,創建一個模板對象。
第五步:創建一個模板使用的數據集,可以是pojo也可以是map。一般是Map。
第六步:創建一個Writer對象,一般創建一FileWriter對象,指定生成的文件名。
第七步:調用模板對象的process方法輸出文件。
第八步:關閉流。
如下面一個簡單的例子。
模本文件:
hello.ftl
內容:
${hello}
如下是freemarker加載模板並生成對應html文件。
@Test public void genFile() throws Exception { // 第一步:創建一個Configuration對象,直接new一個對象。構造方法的參數就是freemarker對於的版本號。 Configuration configuration = new Configuration(Configuration.getVersion()); // 第二步:設置模板文件所在的路徑。 configuration.setDirectoryForTemplateLoading(new File("D:/workspaces-itcast/term197/taotao-item-web/src/main/webapp/WEB-INF/ftl")); // 第三步:設置模板文件使用的字符集。一般就是utf-8. configuration.setDefaultEncoding("utf-8"); // 第四步:加載一個模板,創建一個模板對象。 Template template = configuration.getTemplate("hello.ftl"); // 第五步:創建一個模板使用的數據集,可以是pojo也可以是map。一般是Map。 Map dataModel = new HashMap<>(); //向數據集中添加數據 dataModel.put("hello", "this is my first freemarker test."); // 第六步:創建一個Writer對象,一般創建一FileWriter對象,指定生成的文件名。 Writer out = new FileWriter(new File("D:/temp/term197/out/hello.html")); // 第七步:調用模板對象的process方法輸出文件。 template.process(dataModel, out); // 第八步:關閉流。 out.close(); }
3.3. 模板的語法
3.3.1. 訪問map中的key
${key}
3.3.2. 訪問pojo中的屬性
Student對象。學號、姓名、年齡
${key.property}
3.3.3. 取集合中的數據
<#list studentList as student> ${student.id}/${studnet.name} </#list>
循環使用格式:
<#list 要循環的數據 as 循環后的數據> </#list>
3.3.4. 取循環中的下標
<#list studentList as student> ${student_index} </#list>
3.3.5. 判斷
<#if student_index % 2 == 0> <#else> </#if>
3.3.6. 日期類型格式化
直接取值:${date}(date是屬性名)如果傳來的是一個Date型數據會報錯
${date?date} 2016-9-13
${date?time} 17:53:55 ${date?datetime} 2016-9-13 17:53:55
3.3.7. Null值的處理
如果直接取一個不存在的值(值為null)時會報異常
${aaa}
處理: ${aaa!”默認值”}或者${aaa! }代表空字符串
3.3.8. Include標簽
<#include “模板名稱”>
(相當於jstl中的包含)
3.4. Freemarker整合spring
引入jar包:
Freemarker的jar包
3.4.1. 創建整合spring的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/ftl/" /> <property name="defaultEncoding" value="UTF-8" /> </bean> </beans>
需要編寫一Controller進行測試
3.4.2. Controller
請求的url:/genhtml
參數:無
返回值:ok (String, 需要使用@ResponseBody)
業務邏輯:
1、從spring容器中獲得FreeMarkerConfigurer對象。
2、從FreeMarkerConfigurer對象中獲得Configuration對象。
3、使用Configuration對象獲得Template對象。
4、創建數據集
5、創建輸出文件的Writer對象。
6、調用模板對象的process方法,生成文件。
7、關閉流。
加載配置文件:
@Controller public class HtmlGenController { @Autowired private FreeMarkerConfigurer freeMarkerConfigurer; @RequestMapping("/genhtml") @ResponseBody public String genHtml()throws Exception { // 1、從spring容器中獲得FreeMarkerConfigurer對象。 // 2、從FreeMarkerConfigurer對象中獲得Configuration對象。 Configuration configuration = freeMarkerConfigurer.getConfiguration(); // 3、使用Configuration對象獲得Template對象。 Template template = configuration.getTemplate("hello.ftl"); // 4、創建數據集 Map dataModel = new HashMap<>(); dataModel.put("hello", "1000"); // 5、創建輸出文件的Writer對象。 Writer out = new FileWriter(new File("D:/temp/term197/out/spring-freemarker.html")); // 6、調用模板對象的process方法,生成文件。 template.process(dataModel, out); // 7、關閉流。 out.close(); return "OK"; } }
3.5. 商品詳情頁面靜態化
3.5.1. 網頁的靜態化方案
輸出文件的名稱:商品id+“.html”
輸出文件的路徑:工程外部的任意目錄。
網頁訪問:使用nginx訪問網頁。在此方案下tomcat只有一個作用就是生成靜態頁面。
工程部署:可以把taotao-item-web部署到多個服務器上。
生成靜態頁面的時機:商品添加后,生成靜態頁面。可以使用Activemq,訂閱topic(商品添加)
集群環境下,多台服務器提供服務,必須配置一台反向代理服務器,面向用戶,對用戶的請求進行轉發,並提供負載均衡。在這里,http服務器(訪問靜態資源)和反向代理服務器都是Nginx。
4. 內容總結:
商品詳情模塊實現
通過solr全文搜索找到商品;通過商品id去redis中找當前id的緩存,找不到去數據庫中查找並添加到緩存中;
(為了提高redis的高可用,把不常訪問的商品從redis緩存中清除:使用定時)
每次點擊都會把key的時間重置,當key在他的生命中沒有被點擊就會從redis中清除,再次訪問時再次添加
兩方面影響用戶訪問速度:
數據庫查詢
使用緩存
服務器生成html頁面
使用freemaker生成靜態頁面
Freemaker生成靜態頁面的時機
添加商品后使用activemq廣播消息,freemaker監聽到消息去數據庫查詢商品生成靜態頁面
為什么不去redis中獲取商品信息,添加商品時還沒有存到redis中
為什么不直接使用商品信息還要到數據庫中查詢:不在一個項目中傳輸數據麻煩,也起不到提高效率的作用;而且修改數據時也要修改靜態頁面
Redis存儲數據庫表信息;
Key: 表名:id:字段
Value: 字段值
兩種方案:
一 redis緩存
二 網頁靜態化