1. 项目背景与核心需求
去年接手的一个影视平台重构项目让我对SpringBoot+Vue的全栈开发有了全新认识。当时老系统采用的是传统JSP+Servlet架构,面临并发性能差、前后端耦合严重、移动端适配困难三大痛点。这次我们决定用SpringBoot+Vue前后端分离方案重构,目标实现一个支持多端访问、具备弹性扩展能力的影视资源管理系统。
这个系统需要解决的核心问题包括:
- 影视资源的统一元数据管理(标题、分类、主演、简介等)
- 多格式视频文件的转码与自适应播放
- 用户分级权限与内容审核流程
- 高并发场景下的资源分发优化
- 多终端(Web/App/小程序)的API兼容设计
2. 技术选型与架构设计
2.1 后端技术栈
选择SpringBoot 2.7.x作为基础框架,主要基于以下考虑:
- 内嵌Tomcat简化部署,默认支持HTTP/2
- Starter机制快速集成MyBatis Plus、Redis等组件
- Actuator提供完善的监控端点
- 与SpringCloud生态无缝对接便于后期扩展
数据库采用MySQL 8.0+Redis 7的组合:
- MySQL存储结构化数据(用户信息、影视元数据)
- Redis处理热点数据缓存和会话管理
- 使用Redisson实现分布式锁控制资源并发修改
2.2 前端技术栈
Vue 3组合式API相比Options API更适合复杂业务场景:
- 按功能组织代码(播放器组件、用户中心模块等)
- Composition API更好的TypeScript支持
- Vite构建速度比Webpack快5-8倍
播放器方案对比:
- video.js:功能全面但包体积较大
- DPlayer:轻量但插件生态有限
- 最终选择hls.js+m3u8方案,支持:
- 自适应码率切换
- 分段加载优化
- DRM基础保护
3. 核心功能实现细节
3.1 影视资源管理模块
采用DDD分层架构设计:
code复制application/
├── command/ # CQRS命令
├── query/ # 查询服务
domain/
├── model/ # 聚合根
├── repository/ # 仓储接口
infrastructure/
├── dao/ # MyBatis实现
├── cache/ # Redis操作
影视元数据建模示例:
java复制public class Video {
private Long id;
private String title;
private VideoType type; // 电影/电视剧/纪录片
private List<Actor> actors;
private List<VideoTag> tags;
private VideoStatus status; // 审核状态
private LocalDateTime uploadTime;
private List<VideoFile> files; // 不同清晰度文件
}
3.2 视频处理流水线
上传后的视频会进入处理队列:
- FFmpeg进行转码(1080p/720p/480p)
- 生成HLS分片(.ts文件+m3u8索引)
- 提取关键帧生成预览图
- 写入CDN边缘节点
使用Spring Batch实现分布式转码:
java复制@Bean
public Step transcodeStep() {
return stepBuilderFactory.get("transcode")
.<VideoFile, VideoFile>chunk(10)
.reader(videoItemReader())
.processor(ffmpegProcessor())
.writer(cdnUploadWriter())
.taskExecutor(taskExecutor())
.throttleLimit(5)
.build();
}
3.3 播放鉴权与统计
JWT+RBAC实现权限控制:
vue复制// 前端路由守卫
router.beforeEach((to) => {
if (to.meta.requiresVIP && !store.state.user.vip) {
return '/upgrade'
}
})
播放统计采用滑动窗口算法:
java复制// 防止刷量
public boolean checkPlayFrequency(Long userId) {
String key = "play:limit:" + userId;
long count = redisTemplate.opsForZSet()
.count(key, System.currentTimeMillis() - 3600000, System.currentTimeMillis());
return count < 100; // 1小时内不超过100次
}
4. 性能优化实践
4.1 缓存策略设计
采用多级缓存架构:
- 本地Caffeine缓存(过期时间5分钟)
- Redis集群缓存(过期时间2小时)
- MySQL持久化存储
缓存击穿解决方案:
java复制public Video getVideoWithCache(Long id) {
String key = "video:" + id;
return redisTemplate.execute(new RedisCallback<Video>() {
@Override
public Video doInRedis(RedisConnection connection) {
// 1. 先查Redis
byte[] value = connection.get(key.getBytes());
if (value != null) {
return deserialize(value);
}
// 2. 获取分布式锁
if (tryLock(key)) {
try {
// 3. 查数据库
Video video = videoMapper.selectById(id);
// 4. 写缓存
connection.setEx(key.getBytes(), 7200, serialize(video));
return video;
} finally {
unlock(key);
}
}
// 5. 锁竞争时短暂休眠后重试
Thread.sleep(100);
return getVideoWithCache(id);
}
});
}
4.2 接口性能调优
针对/video/list接口的优化:
- 启用MyBatis二级缓存
- 使用SQL窗口函数优化分页:
sql复制SELECT * FROM (
SELECT v.*,
ROW_NUMBER() OVER(ORDER BY upload_time DESC) AS rn
FROM video v
WHERE status = 'PUBLISHED'
) t WHERE rn BETWEEN 1 AND 20
- 响应数据压缩(gzip)
- 静态资源走CDN
优化前后对比:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| QPS | 120 | 850 |
| 平均响应时间 | 450ms | 65ms |
| 99线 | 1.2s | 200ms |
5. 安全防护方案
5.1 常见攻击防护
XSS防护方案:
- 前端使用DOMPurify过滤富文本
- 后端统一响应头:
java复制http.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
CSRF防护采用SameSite Cookie+双重提交:
vue复制// 前端axios拦截器
axios.interceptors.request.use(config => {
config.headers['X-CSRF-TOKEN'] = store.state.csrfToken
return config
})
5.2 视频防盗链措施
- 时间戳+签名验证:
code复制http://cdn.example.com/video.m3u8?
t=1645584000&
sign=md5(secretKey + path + t)
- Referrer白名单校验
- HLS加密(AES-128):
bash复制ffmpeg -i input.mp4 -hls_key_info_file encrypt.keyinfo -hls_playlist_type vod output.m3u8
6. 部署与监控
6.1 容器化部署
Docker Compose编排方案:
yaml复制services:
app:
image: openjdk:17-jdk
deploy:
resources:
limits:
cpus: '2'
memory: 2G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
redis:
image: redis:7-alpine
command: redis-server --save 60 1 --loglevel warning
6.2 监控指标采集
Prometheus监控配置:
java复制@Bean
public MeterRegistryCustomizer<PrometheusMeterRegistry> configureMetrics() {
return registry -> {
registry.config().commonTags("application", "video-service");
new JvmMemoryMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
};
}
关键告警规则:
- 视频转码队列积压 > 100
- 播放错误率 > 1%
- 接口99线 > 500ms
- JVM Old Gen使用率 > 80%
7. 典型问题解决方案
7.1 播放卡顿优化
问题现象:部分用户播放时频繁缓冲
排查过程:
- 检查CDN节点分布(发现海外节点不足)
- 分析m3u8分片大小(默认10s改为6s)
- 增加ABR(自适应码率)策略
最终方案:
javascript复制// hls.js配置
const hls = new Hls({
maxBufferLength: 30,
maxMaxBufferLength: 600,
abrEwmaDefaultEstimate: 500000
});
7.2 高并发写入冲突
场景:热门视频的收藏数更新
解决方案:Redis原子操作+异步落库
java复制public void incrementFavorite(Long videoId) {
String key = "video:fav:" + videoId;
redisTemplate.opsForValue().increment(key);
// 异步任务每5分钟刷到数据库
scheduleExecutor.schedule(() -> {
Long delta = redisTemplate.opsForValue().getAndDelete(key);
videoMapper.updateFavoriteCount(videoId, delta);
}, 5, TimeUnit.MINUTES);
}
8. 项目演进方向
-
智能推荐系统
- 用户行为画像(播放完成度、暂停点等)
- 协同过滤算法优化
- 实时推荐接口
-
多CDN智能调度
- 基于QoE(体验质量)的节点选择
- TCP加速优化
- QUIC协议支持
-
边缘计算方案
- 将转码任务下沉到边缘节点
- 基于用户位置的缓存预热
这个项目让我深刻体会到,影视类系统开发不仅是CRUD那么简单,需要综合考虑编解码技术、分布式架构、版权保护等多个维度。特别是在处理高并发播放请求时,每个环节的优化都能带来明显的体验提升。建议后续可以引入WebAssembly来优化前端播放器的解码性能,这可能是下一个技术突破点。
