页面静态化(模板引擎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…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
