页面静态化(模板引擎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…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
