页面静态化(模板引擎Freemarker)


1、浏览器请求web服务器
2、服务器渲染页面,渲染的过程就是向jsp页面(模板)内填充数据(模型)。
3、服务器将渲染生成的页面返回给浏览器。
所以模板引擎就是:模板+数据=输出,Jsp页面就是模板,页面中嵌入的jsp标签就是数据,两者相结合输出html网页。
Freemarker
添加依赖:
<!-- Spring Boot 对结果视图 Freemarker 集成 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
配置文件
YAML
spring:freemarker:enabled: truecache: false #关闭模板缓存,方便测试settings:template_update_delay: 0suffix: .ftl #页面模板后缀名charset: UTF-8template-loader-path: classpath:/templates/ #页面模板位置(默认为 classpath:/templates/)resources:add-mappings: false #关闭项目中的静态资源映射(static、resources文件夹下的资源)
Html页面
HTML
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>Hello World!</title>
</head>
<body>
Hello ${name}!
</body>
</html>
接口实现
@Controller
public class FreemarkerController {@GetMapping("/testfreemarker")public ModelAndView test(){ModelAndView modelAndView = new ModelAndView();//设置模型数据modelAndView.addObject("name","小明");//设置模板名称modelAndView.setViewName("test");return modelAndView;}
public void testGenerateHtmlByTemplate() throws IOException, TemplateException {//配置freemarkerConfiguration configuration = new Configuration(Configuration.getVersion());//加载模板//选指定模板路径,classpath下templates下//得到classpath路径String classpath = this.getClass().getResource("/").getPath();configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/"));//设置字符编码configuration.setDefaultEncoding("utf-8");//指定模板文件名称Template template = configuration.getTemplate("course_template.ftl");//准备数据CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(2L);Map<String, Object> map = new HashMap<>();map.put("model", coursePreviewInfo);//静态化//参数1:模板,参数2:数据模型String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);System.out.println(content);//将静态化内容输出到文件中InputStream inputStream = IOUtils.toInputStream(content);//输出流FileOutputStream outputStream = new FileOutputStream("D:\\develop\\test.html");IOUtils.copy(inputStream, outputStream);}}
course_template.html是一个静态html页面,里边还没有添加freemarker标签,如果要预览该页面需要借助Nginx进行预览,因为页面需要加载一些css样式表、图片等内容。
在进行课程预览时需要展示课程的图片,在线插放课程视频,课程图片、视频这些都在MinIO文件系统存储,下边统一由Nginx代理,通过文件服务域名统一访问。
进入课程详情页面,点击马上学习或课程目录下的小节的名称将打开视频播放页面。首先在nginx.conf中配置视频播放页面的地址。
开发流程:
1、请求参数
传入课程id,表示要预览哪一门课程。
2、响应结果
输出课程详情页面到浏览器。
响应页面到浏览器使用freemarker模板引擎技术实现,首先从课程资料目录下获取课程预览页面course_template.html,拷贝至内容管理的接口工程的resources/templates下,并将其在本目录复制一份命名为course_template.ftl
@GetMapping("/coursepreview/{courseId}")public ModelAndView preview(@PathVariable("courseId") Long courseId) {ModelAndView modelAndView = new ModelAndView();//查询课程的信息作为模型数据CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId);//指定模型modelAndView.addObject("model", coursePreviewInfo);//指定模板modelAndView.setViewName("course_template");//根据视图名称加.ftl找到模板return modelAndView;}

课程审核


说明如下:
1、一门课程新增后它的审核状为”未提交“,发布状态为”未发布“。
2、课程信息编辑完成,教学机构人员执行”提交审核“操作。此时课程的审核状态为”已提交“。
3、当课程状态为已提交时运营平台人员对课程进行审核。
4、运营平台人员审核课程,结果有两个:审核通过、审核不通过。
5、课程审核过后不管状态是通过还是不通过,教学机构可以再次修改课程并提交审核,此时课程状态为”已提交“。此时运营平台人员再次审核课程。
6、课程审核通过,教学机构人员可以发布课程,发布成功后课程的发布状态为”已发布“。
7、课程发布后通过”下架“操作可以更改课程发布状态为”下架“
8、课程下架后通过”上架“操作可以再次发布课程,上架后课程发布状态为“发布”。

提交课程审核,将课程信息汇总后写入课程预发布表,课程预发布表记录了教学机构在某个时间点要发布的课程信息。
课程审核人员从预发布表查询信息进行审核。
课程审核的同时可以对课程进行修改,修改的内容不会写入课程预发布表。
课程审核通过执行课程发布,将课程预发布表的信息写入课程发布表。
提交审核课程后,必须等到课程审核完成才可以再次提交课程。
课程发布

1、向内容管理数据库的课程发布表存储课程发布信息,更新课程基本信息表中发布状态为已发布。
2、向Redis存储课程缓存信息。
3、向Elasticsearch存储课程索引信息。
4、请求分布文件系统存储课程静态化页面(即html页面),实现快速浏览课程详情页面。
redis中的课程缓存信息是将课程发布表中的数据转为json进行存储。
elasticsearch中的课程索引信息是根据搜索需要将课程名称、课程介绍等信息进行索引存储。
MinIO中存储了课程的静态化页面文件(html网页),查看课程详情是通过文件系统去浏览课程详情页面。
分布式事务控制
满足CP?
如果要满足CP就表示课程发布操作后向数据库、redis、elasticsearch、MinIO写四份数据,只要有一份写失败其它的全部回滚。
满足AP?
课程发布操作后,先更新数据库中的课程发布状态,更新后向redis、elasticsearch、MinIO写课程信息,只要在一定时间内最终向redis、elasticsearch、MinIO写数据成功即可。
目前我们已经有了任务调度的技术积累,这里选用任务调度的方案去实现分布式事务控制,课程发布满足AP即可。

1、在内容管理服务的数据库中添加一个消息表,消息表和课程发布表在同一个数据库。
2、点击课程发布通过本地事务向课程发布表写入课程发布信息,同时向消息表写课程发布的消息。通过数据库进行控制,只要课程发布表插入成功消息表也插入成功,消息表的数据就记录了某门课程发布的任务。
3、启动任务调度系统定时调度内容管理服务去定时扫描消息表的记录。
4、当扫描到课程发布的消息时即开始完成向redis、elasticsearch、MinIO同步数据的操作。
5、同步数据的任务完成后删除消息表记录。
时序图如下:
下图是课程发布操作的流程:

1、执行发布操作,内容管理服务存储课程发布表的同时向消息表添加一条“课程发布任务”。这里使用本地事务保证课程发布信息保存成功,同时消息表也保存成功。
2、任务调度服务定时调度内容管理服务扫描消息表,由于课程发布操作后向消息表插入一条课程发布任务,此时扫描到一条任务。
3、拿到任务开始执行任务,分别向redis、elasticsearch及文件系统存储数据。
4、任务完成后删除消息表记录。

下边对消息SDK的设计内容进行说明:
sdk需要提供执行任务的逻辑吗?
拿课程发布任务举例,执行课程发布任务是要向redis、索引库等同步数据,其它任务的执行逻辑是不同的,所以执行任务在sdk中不用实现任务逻辑,只需要提供一个抽象方法由具体的执行任务方去实现。
如何保证任务的幂等性?
在视频处理章节介绍的视频处理的幂等性方案,这里可以采用类似方案,任务执行完成后会从消息表删除,如果消息的状态是完成或不存在消息表中则不用执行。
如何保证任务不重复执行?
采用和视频处理章节一致方案,除了保证任务的幂等性外,任务调度采用分片广播,根据分片参数去获取任务,另外阻塞调度策略为丢弃任务。
注意:这里是信息同步类任务,即使任务重复执行也没有关系,不再使用抢占任务的方式保证任务不重复执行。
还有一个问题,根据消息表记录是否存在或消息表中的任务状态去保证任务的幂等性,如果一个任务有好几个小任务,比如:课程发布任务需要执行三个同步操作:存储课程到redis、存储课程到索引库,存储课程页面到文件系统。如果其中一个小任务已经完成也不应该去重复执行。这里该如何设计?
将小任务作为任务的不同的阶段,在消息表中设计阶段状态。

每完成一个阶段在相应的阶段状态字段打上完成标记,即使这个大任务没有完成再重新执行时,如果小阶段任务完成了也不会重复执行某个小阶段的任务。
远程调用
<!-- Spring Cloud 微服务远程调用 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId>
</dependency>
<!--feign支持Multipart格式传参-->
<dependency><groupId>io.github.openfeign.form</groupId><artifactId>feign-form</artifactId><version>3.8.0</version>
</dependency>
<dependency><groupId>io.github.openfeign.form</groupId><artifactId>feign-form-spring</artifactId><version>3.8.0</version>
</dependency>
feign:hystrix:enabled: truecircuitbreaker:enabled: true
hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 30000 #熔断超时时间
ribbon:ConnectTimeout: 60000 #连接超时时间ReadTimeout: 60000 #读超时时间MaxAutoRetries: 0 #重试次数MaxAutoRetriesNextServer: 1 #切换实例的重试次数
//在启动类上
@EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"})
@FeignClient(value = "search",fallbackFactory = SearchServiceClientFallbackFactory.class)
public interface SearchServiceClient {@PostMapping("/search/index/course")public Boolean add(@RequestBody CourseIndex courseIndex);
}
降级处理
@FeignClient(value = "media-api",configuration =
MultipartSupportConfig.class,fallback = MediaServiceClientFallback.class)
@RequestMapping("/media")
public interface MediaServiceClient{
Java
@FeignClient(value = "media-api",configuration = MultipartSupportConfig.class,
fallbackFactory = MediaServiceClientFallbackFactory.class)
@Slf4j
@Component
public class MediaServiceClientFallbackFactory implements FallbackFactory<MediaServiceClient> {@Overridepublic MediaServiceClient create(Throwable throwable) {return new MediaServiceClient(){@Overridepublic String uploadFile(MultipartFile upload, String objectName) {//降级方法log.debug("调用媒资管理服务上传文件时发生熔断,异常信息:{}",throwable.toString(),throwable);return null;}};}
}
elasticsearch
相关文章:
页面静态化(模板引擎Freemarker)
1、浏览器请求web服务器 2、服务器渲染页面,渲染的过程就是向jsp页面(模板)内填充数据(模型)。 3、服务器将渲染生成的页面返回给浏览器。 所以模板引擎就是:模板数据输出,Jsp页面就是模板,页面中嵌入的jsp标签就是数据&#x…...
详细记录Pycharm配置已安装好的Conda虚拟环境
当安装好conda环境之后,想要在Pycharm中使用,那么就要在Pycharm中导入,我这里使用的pycharm-professional-2023.2这个版本,下面是详细步骤: 1.打开File->Settings: 2.找到Project——>Python Inter…...
photoshop生成器引入到electron项目(electron与photoshop建立通信)
Photoshop引入了nodejs,在启动的时候,通过pipe调起nodejs运行时核心generator-builtin,通过KLVR机制与ps进行通信和交互,同时会加载用户编写的扩展。 这里记录一下引入时的踩坑过程 generator-core就是它的源码,elect…...
Stable Diffuion webui Mac版本安装过程
系统环境 操作系统:MacOS Ventura13.5 芯片:Apple M2 Max Python: 3.10 安装前置准备 git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git注意事项:修改源码内全部 git clone 链接,设置代理 https://ghpr…...
ARM64 指令用法学习整理
1. CBZ 当我们谈论ARM64指令集时,CBZ(Compare and Branch on Zero)是一种条件分支指令。它用于在寄存器上进行比较,并且如果该寄存器的值为零,则跳转到指定的标签或地址。 CBZ指令的基本语法如下: CBZ &…...
stable-diffusion 模型效果+prompt
摘自个人印象笔记,图不完整可查看原笔记:https://app.yinxiang.com/fx/55cda0c6-2af5-4d66-bd86-85da79c5574ePrompt运用规则及技巧 : 1. https://publicprompts.art/(最适用于OpenArt 线上模型 https://openart.ai/)…...
uniapp 小兔鲜儿 - 首页模块(1)
目录 自定义导航栏 静态结构 安全区域 通用轮播组件 静态结构 自动导入全局组件 全局组件类型声明 .d.ts文件 注册组件 vue/runtime-core 首页 – 轮播图指示点 首页 – 获取轮播图数据 首页 – 轮播图数据类型并渲染 首页 – 轮播图总结 首页分类 首页 – 前…...
selenium.webdriver Python爬虫教程
文章目录 selenium安装和使用 selenium安装和使用 pip install selenium 下载对应的浏览器驱动 实例化浏览器 from selenium import webdriverbrowser webdriver.Chrome()元素定位 控制浏览器...
USB-SC-09编程电缆驱动程序安装说明
USB-SC-09编程电缆驱动程序安装说明 概述 USB编程电缆是通过将电脑的USB口模拟成传统的串行口(通常为COM3),从而使编程软件SWOPC-FXGP/WIN和GPP通过USB-SC-09编程电缆与FX系列PLC进行通信。 功能 ● 支持的操作系统Windows XP/ Windows2000 …...
ONVIF对讲功能漫谈
ONVIF对讲功能漫谈 前言一、onvif对讲功能和onvif协议关系大吗?二、如何上报设备支持onvif对讲功能呢?三、onvif协议中哪个接口上报音频解码配置?四、献上抓包报文:前言 本篇文章尝试使用提问的方式来分享onvif对讲功能那点事。 一、onvif对讲功能和onvif协议关系大吗? on…...
计算文本相似度
目录 Python中的difflib模块模块用法报告涉及的符号实现文本对比普通文本对比文本对比生成HTML报告 余弦相似度sklearn安装使用sklearn的余弦相似度词袋模型 Jaccard相似度编辑距离(Levenshtein距离)TF-IDFWord2VecDoc2VecBERT结论 Python中的difflib模块…...
oracle 增加控制文件
oracle 增加控制文件 1、看control_file路径 SQL> show parameter controlNAME TYPE VALUE ------------------------------------ ----------- ------------------------------ control_file_record_keep_time integer …...
OpenFeign超时时间设置不生效问题排查
最近升级springboot 3,突然发现配置文件中的openFeign的超时时间设置不生效了 之前配置如下: feign: client: config: default: connectTimeout: 3000 readTimeout: 5000 查资料都是说ribbon或者hystrix的超时时间设置问…...
Go和Java实现原型模式
Go和Java实现原型模式 下面将通过一个克隆的示例来说明原型模式的使用。 1、原型模式 原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对 象的最佳方式之一。 这种模式是实现了一个原型接口&am…...
linux I/O性能优化
Linux 文件系统 磁盘和文件系统的关系: 磁盘为系统提供了最基本的持久化存储。 文件系统则在磁盘的基础上,提供了一个用来管理文件的树状结构。 文件系统工作原理 索引节点和目录项 文件系统,本身是对存储设备上的文件,进行组织…...
PHP最简单自定义自己的框架model使用(七)
1、实现model使用效果 2、自动加载model,KJ.php //自动加载文件public static function _autoload($className){switch ($className){//自动model类case substr($className,-5)Model:$path MODEL./.$className..php;if(is_file($path)) include $path;break;//自动加载控制器…...
程序猿成长之路之密码学篇-分组密码加密模式及IV(偏移量)的详解
Cipher.getInstance("AES/ECB/PKCS5Padding"); Cipher cipher Cipher.getInstance("AES/CBC/PKCS5Padding"); 在进行加解密编程的时候应该有很多小伙伴接触过以上的语句,但是大伙儿在编码过程中是否了解过ECB/CBC的含义、区别以及PKCS5Padding…...
Windows下批处理删除文件
最近我使用Maven的时候会出现下载jar包不成功的现象,然后需要把它删除然后重新下载,但是有时候文件过多,一个个删除太花费时间,所以用bat的批处理会很舒服。 bat的语法我之前没遇到过,然后我是边学习边试验࿰…...
html中文件上传储存到本地路径
第一步:写html文件 <form action"/uplode" method"post" enctype"multipart/form-data">姓名:<input type"text" name"username"><br>年龄:<input type"text" name"age"><…...
第九章 SpringBoot 自动配置原理 入门
1. 引导加载自动配置类 SpringBootApplication -- SpringBootConfiguration -- EnableAutoConfiguration -- ComponentScan //SpringBootApplicationSpringBootConfiguration EnableAutoConfiguration ComponentScan(excludeFilters { Filter(type FilterType.CUSTOM, cl…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...
