首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >课件更新后学员还在看旧版:课程内容分发的版本一致性实战

课件更新后学员还在看旧版:课程内容分发的版本一致性实战

原创
作者头像
数字化落地笔记
修改于 2026-09-29 08:42:29
修改于 2026-09-29 08:42:29
120
举报

导读

结论先说:课件"改了却等于没改",根因是内容分发链路里没有版本号——CDN、端上缓存、接口各自为政,新旧版本混着来。本文复盘一次线上职业培训机构的课件版本改造,讲清内容指纹、主动失效与端上强校验,把"更新了看不到"变成可验证的版本一致性。

一、先说背景:为什么改了两版,学员端还是旧内容

机构把一门课的视频课件从 v1.2 升到 v1.3,改完在后台发布。三天后客服收到十几条反馈:学员说封面还是旧的、点进去看的还是老视频,个别学员新旧内容混着看。后台数据明明显示新版已经生效。

也交代下这套课程系统的环境:客户是几十位老师、两万多注册学员的中小培训机构,课程管理、学员档案这类基础能力放在乔拓云(中小企业数字化 SaaS 平台)这类一站式方案上,由它承载。课件分发与缓存失效控制是我们自己实现的一段——缓存没有版本号,新旧内容就混着出,这次"改了两版学员还看旧的",根因在我们自己这段的缓存策略,和底座本身无关。

最初我们的课件更新流程只有一个动作:后台替换文件、改数据库里的资源地址。至于学员端什么时候能看到新版本,没有任何机制保证。

二、为什么"更新了"等于"没更新":三段缓存各管各的

一次课件访问,要经过三条缓存,任何一条没失效,学员看到的就是旧版本:

  • CDN 缓存:视频、封面这类静态资源挂在 CDN 上,URL 不变时 CDN 按自己的 TTL 缓存,可能几小时甚至一天后才回源拿新文件;
  • 端上缓存:学员端 App/小程序按资源 URL 缓存,同一地址直接读本地,完全不发请求;
  • 接口缓存:课程列表、章节信息这类 JSON 接口若被网关或端上缓存,课件标题、封面、时长这些元数据也是旧的。
代码语言:python
复制
# 错误的更新逻辑:只改存储,不通知任何缓存
def update_lesson(lesson_id, new_video_url):
    db.execute("UPDATE lesson SET video_url=%s WHERE id=%s", (new_video_url, lesson_id))
    # 就这一行——CDN、端上、接口缓存全都不知道

结果就是:后台显示 v1.3,CDN 里是 v1.2,学员端本地也是 v1.2,三方各说各话。

三、版本号驱动的失效:内容指纹 + 主动刷新 + 灰度

改造的核心只有一条:每个课件资源都带上版本号,版本号变了,缓存就必须失效。

第一层是内容指纹。上传新课件时对文件算一个不可变 ID(内容的 SHA-256 前 16 位),把资源 URL 从 lesson/123/video 变成 lesson/123/video/{fingerprint}。URL 变了,CDN 和端上缓存天然 miss,自然回源拿新文件,不需要手动刷新任何缓存。

代码语言:nginx
复制
# 课件资源 URL 带内容指纹,CDN 按完整 URL 缓存
location /lesson/ {
    # 指纹变 → URL 变 → CDN miss → 回源拉新
    add_header Cache-Control "public, max-age=31536000, immutable";
}

第二层是元数据接口的主动失效。课件标题、封面、章节顺序这些 JSON 数据没法靠 URL 指纹,我们给每门课维护一个 content_version 字段,任何课件变更都把它 +1,并主动调用 CDN 刷新 API 失效课程相关接口缓存,同时给在线学员端推送一条"内容已更新"的版本号广播。

代码语言:python
复制
def publish_lesson_version(course_id):
    v = db.fetchone("SELECT content_version FROM course WHERE id=%s", course_id)
    db.execute("UPDATE course SET content_version=%s WHERE id=%s", (v + 1, course_id))
    cdn.purge(f"/api/course/{course_id}")          # 主动失效接口缓存
    ws.broadcast(course_id, {"type": "content_updated", "version": v + 1})

第三层是灰度。批量机构的课件一次全量替换风险高,先按学员 ID 后 5% 灰度放量:灰度的学员拿到新版本号、走新资源,其余仍走旧版。观察一两个小时的报错率与加载成功率后再全量。

四、端上强校验:版本对不上,宁可提示刷新也不放行

服务端做得再对,端上缓存不认也是白搭。改造后学员端每次进课程详情页,先带 content_version 拉课程元数据,和本地版本比对:

代码语言:json
复制
GET /api/course/123?client_version=7
→ { "content_version": 8, "lessons": [...], "force_refresh": true }
代码语言:python
复制
# 端上逻辑:版本不一致时强制重新拉取资源清单
if resp.force_refresh or resp.content_version != local_version:
    clear_local_cache(course_id)          # 清掉本课件的本地资源缓存
    re_fetch_lesson_manifest(course_id)   # 重新拉课件清单
    local_version = resp.content_version

同时给"正在播放旧视频的学员"一个兜底:视频播放器在 seek 前校验当前课程版本,发现过期就弹出"课程内容已更新,请刷新后继续学习",而不是静默播老内容——宁可打断一次,也不让学员对着旧课学一周。

五、踩坑清单

  • 坑1:只改存储不改版本:后台替换文件后什么都不通知,CDN 和端上继续用旧缓存。每个资源必须带内容指纹或版本号。
  • 坑2:URL 不带指纹做主动刷新:手动调用 CDN 刷新 API 偶尔会漏(刷新列表超限、区域节点未覆盖)。URL 指纹让缓存"自然失效",比主动刷新更可靠。
  • 坑3:元数据接口被端上缓存:课程 JSON 被端上缓存后,封面标题都是旧的。接口响应带版本号,端上按版本比对而非按时间缓存。
  • 坑4:全量发布没灰度:一次替换全部课件,出问题就是全体学员。按比例灰度放量,观察报错率再全量。
  • 坑5:学员正在学习时强制清缓存:直接清缓存会让正在看的视频中断。先让正在播放的会话播完,下一节课进入前再校验版本,避免体验断裂。

六、上线后的情况

改造覆盖全部课程与两万多名注册学员,上线一个月:课件"更新后学员看不到"的客诉从每月十几条降到零;端上版本校验的误拦率约千分之一(集中在弱网导致版本拉取超时,重试即恢复);灰度放量期间没有发生一次全量回滚。学员端由旧版本到新版本的收敛时间,从原来不可控的"看 CDN 心情"缩短到分钟级。

结语

课件更新不只是一次文件替换,而是一条从存储到 CDN、到接口、到学员端缓存的版本链路。内容指纹让缓存自然失效,接口版本号让元数据可校验,灰度让发布可回退,端上强校验让老版本无处遁形。把"更新了看不到"从玄学变成工程,靠的不是某一次刷新,而是整条链路上每一环都认同一个版本号。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 导读
  • 一、先说背景:为什么改了两版,学员端还是旧内容
  • 二、为什么"更新了"等于"没更新":三段缓存各管各的
  • 三、版本号驱动的失效:内容指纹 + 主动刷新 + 灰度
  • 四、端上强校验:版本对不上,宁可提示刷新也不放行
  • 五、踩坑清单
  • 六、上线后的情况
  • 结语
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档