学成在线-课程发布
一、课程预览
1.freemarker模板引擎

示例:
1 2 3 4 5 6 7 8 9
| @GetMapping("/testfreemarker") public ModelAndView test(){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("name","小明"); modelAndView.setViewName("test"); return modelAndView; }
|
模板文件如下
1 2 3 4 5 6 7 8 9 10
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello World!</title> </head> <body> Hello ${name}! </body> </html>
|
二、课程发布
1.分布式事务
本地事务是指

分布式事务的出现

分布式事务概念
1
| 分布式事务是指跨越多个网络节点、多个数据库的事务操作。它的挑战在于网络不可靠(比如脑裂、超时、丢包),所以我们必须要在 CAP 定理和 BASE 理论中做出妥协——通常是为了可用性(A)和分区容错性(P),去换取最终一致性。
|
2.CAP理论

但是,这三份中只能选俩个

3.CP 系统

4.AP 系统

1 2 3 4 5 6 7
| 分区发生时:
CP: [Client] --写--> [Leader] --等待多数确认--> 成功/超时报错 节点不可达 → 拒绝服务,保数据正确
AP: [Client] --写--> [任意节点] --立即返回成功--> 后台异步同步 节点不可达 → 继续服务,数据稍后收敛
|
三、课程发布任务实现
1.页面静态化

为了解决高并发的难题
代码实现
- 引入依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
|
- 写模板文件
article.ftl
1 2 3 4 5 6 7 8 9 10
| <!DOCTYPE html> <html> <body> <h1>${title}</h1> <p>${content}</p> <#list tags as tag> <span>${tag}</span> </#list> </body> </html>
|
- 静态化核心代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Service public class StaticPageService {
@Autowired private Configuration freemarkerConfig;
public void generateHtml(String articleId) throws Exception { Map<String, Object> dataModel = new HashMap<>(); Article article = articleService.getById(articleId); dataModel.put("title", article.getTitle()); dataModel.put("content", article.getContent());
Template template = freemarkerConfig.getTemplate("article.ftl");
String outputPath = "/data/html/article_" + articleId + ".html"; File htmlFile = new File(outputPath);
try (FileWriter writer = new FileWriter(htmlFile)) { template.process(dataModel, writer); }
System.out.println("静态页面生成成功: " + outputPath); } }
|
2.上传minio-OpenFeign
发送http请求

这里我们用到了OpenFeign
1 2 3 4
| 1.config配置类 2.请求接口编写 3.启动类注解 4.配置类配置熔断层
|
3.搜索功能实现
Elestatic做索引管理,搜索功能

4.canal数据同步
四、全流程总结

然后这一模块就搞定啦
1.阶段一:触发发布(SDK 实现)
前端点击”课程发布”,请求到内容管理服务,服务做两件事:
- 把课程信息写入课程发布表(存储发布状态)
- 在消息表里插入一条待处理消息(这是可靠消息机制的起点)
2.阶段二:定时扫描任务(XXL-job 实现)
任务调度服务定时触发,内容管理服务扫描消息表,拿到待处理的发布任务,开始”执行任务”主逻辑(图中那个大矩形框)。
3.阶段三:执行任务主逻辑(三件事并行推进)
图中执行任务框里发出三条线,依次完成:
① 课程缓存 → Redis 把课程信息写入 Redis,供前端快速查询。
② 课程索引 → 搜索服务 → Elasticsearch(esstatic 索引实现) 将课程数据同步到搜索服务,最终写入 ES,实现课程搜索功能。
③ 上传课程详情静态页面 → 媒资管理服务 → MinIO(freemarker + minio 实现) 用 Freemarker 模板引擎把课程详情渲染成 HTML 静态文件,再通过 Feign 调用媒资管理服务,上传到 MinIO 对象存储。这就是你刚才修的那个 bug 所在的环节。
4.阶段四:清理消息(SDK 自动实现)
三件事全部完成后,内容管理服务把消息表里对应的记录删除,标志本次发布任务处理完毕。如果中途某步失败,消息不删除,下次定时任务会重试,保证最终一致性。
整体设计思路
1 2 3 4 5 6 7
| 发布请求 → 写消息表(持久化) ↓ 定时扫描 → 幂等执行三个同步任务 ↓ 全部成功 → 删消息(任务完结) ↓ 任意失败 → 消息保留 → 下次重试
|
用消息表 + XXL-job 定时扫描代替直接同步调用,核心目的是解耦 + 保证可靠性——即使 Redis、ES 或 MinIO 某一刻不可用,任务也不会丢,等服务恢复后自动重试