页面静态化,相比不少人都听过,可是咱们为何使用页面静态化和使用页面静态化的好处又有哪些呢?今天这篇文章咱们就来详细的讲讲。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新手学习交流,拉你进技术交流群,一块儿抱团取暖吧~