结论先说:课件"改了却等于没改",根因是内容分发链路里没有版本号——CDN、端上缓存、接口各自为政,新旧版本混着来。本文复盘一次线上职业培训机构的课件版本改造,讲清内容指纹、主动失效与端上强校验,把"更新了看不到"变成可验证的版本一致性。
机构把一门课的视频课件从 v1.2 升到 v1.3,改完在后台发布。三天后客服收到十几条反馈:学员说封面还是旧的、点进去看的还是老视频,个别学员新旧内容混着看。后台数据明明显示新版已经生效。
也交代下这套课程系统的环境:客户是几十位老师、两万多注册学员的中小培训机构,课程管理、学员档案这类基础能力放在乔拓云(中小企业数字化 SaaS 平台)这类一站式方案上,由它承载。课件分发与缓存失效控制是我们自己实现的一段——缓存没有版本号,新旧内容就混着出,这次"改了两版学员还看旧的",根因在我们自己这段的缓存策略,和底座本身无关。
最初我们的课件更新流程只有一个动作:后台替换文件、改数据库里的资源地址。至于学员端什么时候能看到新版本,没有任何机制保证。
一次课件访问,要经过三条缓存,任何一条没失效,学员看到的就是旧版本:
# 错误的更新逻辑:只改存储,不通知任何缓存
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,自然回源拿新文件,不需要手动刷新任何缓存。
# 课件资源 URL 带内容指纹,CDN 按完整 URL 缓存
location /lesson/ {
# 指纹变 → URL 变 → CDN miss → 回源拉新
add_header Cache-Control "public, max-age=31536000, immutable";
}第二层是元数据接口的主动失效。课件标题、封面、章节顺序这些 JSON 数据没法靠 URL 指纹,我们给每门课维护一个 content_version 字段,任何课件变更都把它 +1,并主动调用 CDN 刷新 API 失效课程相关接口缓存,同时给在线学员端推送一条"内容已更新"的版本号广播。
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 拉课程元数据,和本地版本比对:
GET /api/course/123?client_version=7
→ { "content_version": 8, "lessons": [...], "force_refresh": true }# 端上逻辑:版本不一致时强制重新拉取资源清单
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 前校验当前课程版本,发现过期就弹出"课程内容已更新,请刷新后继续学习",而不是静默播老内容——宁可打断一次,也不让学员对着旧课学一周。
改造覆盖全部课程与两万多名注册学员,上线一个月:课件"更新后学员看不到"的客诉从每月十几条降到零;端上版本校验的误拦率约千分之一(集中在弱网导致版本拉取超时,重试即恢复);灰度放量期间没有发生一次全量回滚。学员端由旧版本到新版本的收敛时间,从原来不可控的"看 CDN 心情"缩短到分钟级。
课件更新不只是一次文件替换,而是一条从存储到 CDN、到接口、到学员端缓存的版本链路。内容指纹让缓存自然失效,接口版本号让元数据可校验,灰度让发布可回退,端上强校验让老版本无处遁形。把"更新了看不到"从玄学变成工程,靠的不是某一次刷新,而是整条链路上每一环都认同一个版本号。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。