16 KiB
16 KiB
网络流量逻辑完整梳理
📋 目录
网络架构概览
服务器配置
- 服务器内网IP:
192.168.58.20 - 应用端口:
4320(内网) /3000(Docker容器内) - MinIO内网地址:
192.168.58.20:9000 - MinIO公网域名:
s3.yydd.ai(通过FRP穿透,解析到103.118.245.6) - 应用公网域名:
3d.yydd.ai(通过FRP穿透)
存储位置
| 文件类型 | 存储位置 | 访问方式 |
|---|---|---|
| 预览图 | MinIO S3 | https://s3.yydd.ai/gongxiangpan/由壹点AI/... |
| 视频预览 (720P) | MinIO S3 | https://s3.yydd.ai/gongxiangpan/由壹点AI/video-preview-720p-... |
| 视频片段 (10秒) | MinIO S3 | https://s3.yydd.ai/gongxiangpan/由壹点AI/video-clip-10s-... |
| 视频封面 | MinIO S3 | https://s3.yydd.ai/gongxiangpan/由壹点AI/video-cover-... |
| 视频源文件 | 本地 uploads/ |
/uploads/video-xxx.mp4 |
| 音频源文件 | 本地 uploads/ |
/uploads/audio-source-xxx.mp3 |
| 其他源文件 | 本地 uploads/ |
/uploads/material-xxx.zip |
关键环境变量
S3_ENDPOINT=https://s3.yydd.ai # 服务器访问S3的地址(当前是公网域名)
S3_PUBLIC_DOMAIN=https://s3.yydd.ai/gongxiangpan # 返回给前端的S3公网域名
SERVER_IP=192.168.58.20 # 服务器内网IP
MINIO_API_PORT=9000 # MinIO内网端口
公网/内网访问逻辑
1. 内网检测机制
客户端检测(app/page.tsx)
- 检测时机: 页面加载时,如果访问的是公网域名(
3d.yydd.ai) - 检测方法:
- HTTP环境: 直接尝试访问
http://192.168.58.20:4320/api/network-status- 如果成功且返回的
hostname是192.168.58.20,判定为内网,自动跳转到内网地址
- 如果成功且返回的
- HTTPS环境: 只通过当前域名的
/api/network-status检测- 检查返回的
clientIp是否为内网IP段 - 如果是内网IP,跳转到
http://192.168.58.20:4320
- 检查返回的
- HTTP环境: 直接尝试访问
服务端检测(lib/network-detection.ts)
- 函数:
detectIsIntranet(request: NextRequest) - 检测逻辑:
- 从请求头获取
x-forwarded-for或x-real-ip作为clientIp - 从请求头获取
host作为hostname - 判断
clientIp或hostname是否为内网IP段(10.x.x.x, 172.16-31.x.x, 192.168.x.x) - 任一为内网IP即判定为内网用户
- 从请求头获取
2. URL重写逻辑(lib/url-rewrite.ts)
rewriteStorageUrlForClient(url, request)
- 作用: 将数据库中的内网URL(如
http://localhost:9000/...)替换为可访问的地址 - 逻辑:
- 内网用户: 使用
http://192.168.58.20:9000(内网MinIO地址) - 外网用户: 使用
https://s3.yydd.ai/gongxiangpan(公网域名) - 已为公网域名: 直接返回,不做修改
- 内网用户: 使用
rewriteMaterialStorageUrlsForClient(material, request)
- 作用: 批量重写素材对象中的所有存储URL字段
- 重写字段:
previewUrlvideoCoverUrlvideoPreview540UrlvideoPreviewClipUrlvideoSourceUrldisplayImages[]
proxyHttpUrl(url)
- 作用: 将S3 URL转换为代理URL(
/api/proxy-media?url=...) - 当前状态: 未在素材API中使用,仅用于系统设置中的LOGO URL
首页预览文件流量逻辑
1. 数据获取流程
客户端 (浏览器)
↓ GET /api/materials?pageSize=20&materialType=...
服务端 (app/api/materials/route.ts)
↓ 查询数据库
↓ rewriteMaterialStorageUrlsForClient(material, request)
↓ 根据用户网络位置重写URL
↓ 返回素材列表(包含重写后的URL)
客户端
↓ 渲染 MaterialCard 组件
2. URL重写位置
文件: app/api/materials/route.ts:764
return rewriteMaterialStorageUrlsForClient(normalizeMaterial(formatted), request);
重写逻辑:
- 内网用户:
http://localhost:9000/...→http://192.168.58.20:9000/... - 外网用户:
http://localhost:9000/...→https://s3.yydd.ai/gongxiangpan/... - 已为公网域名: 保持不变
3. 前端使用方式
文件: components/home/MaterialCard.tsx
图片预览
// 直接使用 previewUrl(已重写)
<img src={material.previewUrl} />
视频预览
// 直接使用 videoPreviewClipUrl 或 videoPreview540Url(已重写)
<video src={material.videoPreviewClipUrl || material.videoPreview540Url} />
音频预览
// 新素材: /uploads/audio-source-xxx.mp3(相对路径,不走代理)
// 老素材: https://s3.yydd.ai/...(S3 URL,直接访问)
const audioUrl = zipFileName.startsWith('http')
? zipFileName
: `/uploads/${zipFileName}`
4. 当前流量路径
内网用户
浏览器 → 内网地址 (192.168.58.20:4320)
→ 应用服务器
→ 数据库查询
→ URL重写为内网MinIO地址 (192.168.58.20:9000)
→ 浏览器直接访问内网MinIO ✅ (内网直连,速度快)
外网用户
浏览器 → 公网域名 (3d.yydd.ai) → FRP穿透
→ 应用服务器
→ 数据库查询
→ URL重写为公网S3域名 (s3.yydd.ai)
→ 浏览器直接访问 s3.yydd.ai → FRP穿透 ❌ (带宽受限,速度慢)
预览文件访问流量逻辑
1. 图片预览(previewUrl, displayImages)
当前实现
- 内网用户: 直接访问
http://192.168.58.20:9000/...(内网直连) - 外网用户: 直接访问
https://s3.yydd.ai/...(通过FRP穿透)
流量路径
外网用户:
浏览器 → s3.yydd.ai (DNS解析到 103.118.245.6)
→ FRP服务器 (103.118.245.6)
→ FRP穿透到内网 (192.168.58.20:9000)
→ MinIO服务器
→ 返回文件
→ FRP穿透回外网
→ 浏览器
2. 视频预览(videoPreviewClipUrl, videoPreview540Url)
当前实现
- 与图片预览相同:直接使用S3 URL,未通过代理
- 问题: 外网用户通过FRP访问,10秒片段(可能5-10MB)加载很慢
流量路径
外网用户:
浏览器 → s3.yydd.ai → FRP穿透 → MinIO
❌ 大文件(5-10MB)通过FRP,带宽受限,加载慢
3. 音频预览(zipFileName)
新素材(存储在本地)
浏览器 → /uploads/audio-source-xxx.mp3
→ Next.js静态文件服务
→ 本地文件系统
→ 返回文件
- 内网用户: ✅ 内网直连,速度快
- 外网用户: ❌ 通过FRP穿透应用服务器,速度慢
老素材(存储在S3)
浏览器 → s3.yydd.ai → FRP穿透 → MinIO
❌ 通过FRP,速度慢
下载源文件流量逻辑
1. 下载API流程
文件: app/api/materials/download/route.ts
本地文件(新素材)
// 视频源文件、音频源文件、压缩包
zipFilePath = "/uploads/video-xxx.mp4"
→ 服务器读取本地文件
→ 流式传输给客户端
流量路径:
外网用户:
浏览器 → /api/materials/download?id=xxx
→ FRP穿透到应用服务器
→ 服务器读取本地文件
→ 流式传输
→ FRP穿透回外网
→ 浏览器下载
S3文件(老素材)
// 老素材的源文件可能存储在S3
zipFileName = "https://s3.yydd.ai/..."
→ 服务器fetch S3 URL
→ 流式传输给客户端
流量路径:
外网用户:
浏览器 → /api/materials/download?id=xxx
→ FRP穿透到应用服务器
→ 服务器fetch s3.yydd.ai
→ 服务器通过FRP访问S3(双重FRP!)
→ 流式传输
→ FRP穿透回外网
→ 浏览器下载
❌ 双重FRP穿透,速度极慢
2. 产品全案下载
特殊处理: 从S3获取资产文件(主图/SKU图/详情图)
// 服务器端fetch S3 URL
const response = await fetch(fileUrl); // fileUrl是S3 URL
流量路径:
外网用户:
浏览器 → /api/materials/download?id=xxx&type=all
→ FRP穿透到应用服务器
→ 服务器fetch多个S3文件
→ 每个文件都通过FRP访问S3
→ 打包成ZIP
→ 流式传输
→ FRP穿透回外网
❌ 多个文件,每个都双重FRP,非常慢
当前问题分析
⚠️ 核心问题:服务器访问自己的资源也走FRP穿透
关键理解:
- ✅ 存储桶/系统/数据库都在同一台机器上(192.168.58.20)
- ✅ 理论上所有流量只需要走一次FRP穿透(外网用户访问时)
- ❌ 但当前配置导致服务器访问自己的MinIO也走FRP(浪费且慢)
问题1: 服务器访问自己的MinIO走FRP(最严重)
当前配置:
S3_ENDPOINT=https://s3.yydd.ai # 公网域名,通过FRP穿透
实际流量路径:
服务器 (192.168.58.20)
→ 访问 s3.yydd.ai
→ DNS解析到 103.118.245.6 (FRP服务器)
→ FRP穿透回 192.168.58.20:9000 (MinIO)
→ 返回数据
→ FRP穿透回 103.118.245.6
→ 服务器接收
问题: 服务器访问同一台机器上的MinIO,却要走FRP穿透,完全浪费!
影响场景:
- 上传文件到S3: 服务器 → FRP → 自己的MinIO(浪费)
- 下载API从S3获取文件: 浏览器 → FRP → 服务器 → FRP → 自己的MinIO → FRP → 服务器 → FRP → 浏览器(双重FRP!)
- 产品全案下载: 服务器fetch多个S3文件,每个都走FRP
问题2: 外网用户访问视频/音频预览慢
根本原因:
- 视频预览文件(10秒片段,5-10MB)存储在S3
- 外网用户直接访问
s3.yydd.ai,通过FRP穿透 - FRP带宽受限,大文件加载慢
流量路径:
外网用户浏览器
→ s3.yydd.ai (DNS解析到 103.118.245.6)
→ FRP穿透到 192.168.58.20:9000 (MinIO)
→ 返回文件
→ FRP穿透回外网
→ 浏览器
这是正常的,只需要一次FRP,但大文件通过FRP带宽受限。
影响:
- 首页视频卡片悬停播放卡顿
- 音频预览加载失败或超时
问题3: 音频文件访问路径不一致
新素材: 存储在本地,通过 /uploads/... 访问
- 内网用户: ✅ 快(内网直连)
- 外网用户: ✅ 只需一次FRP(浏览器 → FRP → 应用服务器 → 本地文件)
老素材: 存储在S3,通过 https://s3.yydd.ai/... 访问
- 所有用户: ✅ 只需一次FRP(浏览器 → FRP → MinIO)
问题总结
| 场景 | 当前流量路径 | 应该的流量路径 | 问题 |
|---|---|---|---|
| 外网用户访问预览图 | 浏览器 → FRP → MinIO | 浏览器 → FRP → MinIO | ✅ 正常(一次FRP) |
| 外网用户访问视频预览 | 浏览器 → FRP → MinIO | 浏览器 → FRP → MinIO | ⚠️ 正常但慢(大文件) |
| 外网用户下载本地文件 | 浏览器 → FRP → 应用服务器 → 本地文件 | 浏览器 → FRP → 应用服务器 → 本地文件 | ✅ 正常(一次FRP) |
| 服务器上传到S3 | 服务器 → FRP → 自己的MinIO | 服务器 → 内网直连MinIO | ❌ 浪费!应该内网直连 |
| 下载API从S3获取 | 浏览器 → FRP → 服务器 → FRP → 自己的MinIO | 浏览器 → FRP → 服务器 → 内网直连MinIO | ❌ 双重FRP!应该内网直连 |
优化方案建议
方案A: 服务器使用内网地址访问MinIO + 外网用户通过代理
1. 修改服务器S3配置
# docker-compose.yml
S3_ENDPOINT=http://192.168.58.20:9000 # 改为内网地址
2. 视频/音频URL通过代理访问
// app/api/materials/route.ts
// 在返回素材前,将视频/音频URL转换为代理URL
if (material.videoPreviewClipUrl) {
material.videoPreviewClipUrl = `/api/proxy-media?url=${encodeURIComponent(material.videoPreviewClipUrl)}`
}
3. 优化代理API性能
- 添加HTTP缓存头
- 优化流式传输
- 支持Range请求(视频播放必需)
优点:
- ✅ 服务器到MinIO内网直连,速度快
- ✅ 外网用户通过代理,服务器从内网获取资源后转发
- ✅ 减少FRP带宽占用
缺点:
- ⚠️ 增加服务器负载(需要转发视频/音频流量)
- ⚠️ 需要修改代码
方案B: 仅优化加载策略(不改架构)
1. 视频懒加载
- 只在视频进入视口时加载
- 优先使用更小的预览片段(
videoPreviewClipUrl优先于videoPreview540Url)
2. 优化浏览器缓存
- 添加适当的缓存头
- 使用Service Worker缓存
优点:
- ✅ 不改架构,风险低
- ✅ 实现简单
缺点:
- ⚠️ 提升有限,不能根本解决问题
方案C: 混合方案(推荐)
1. 服务器使用内网地址访问MinIO
S3_ENDPOINT=http://192.168.58.20:9000
2. 外网用户:视频/音频通过代理,图片直接访问
- 视频/音频:通过
/api/proxy-media代理 - 图片:直接访问S3(文件小,影响不大)
3. 内网用户:全部直接访问内网MinIO
- 不通过代理,直接访问
http://192.168.58.20:9000
优点:
- ✅ 兼顾速度和服务器负载
- ✅ 内网用户不受影响
- ✅ 外网用户体验提升明显
总结
当前状态
- ✅ 内网用户:体验良好(内网直连)
- ❌ 外网用户:视频/音频预览加载慢(FRP带宽受限)
- ❌ 服务器访问S3也走FRP(效率低)
推荐方案
方案C(混合方案):
- 服务器使用内网地址访问MinIO
- 外网用户的视频/音频通过代理访问
- 内网用户直接访问内网MinIO
实施步骤
- 修改
S3_ENDPOINT为内网地址 - 在素材API中,检测用户网络位置
- 外网用户的视频/音频URL转换为代理URL
- 优化代理API性能(缓存、流式传输)
- 测试验证
💡 关键理解总结
你的理解 ✅ 基本正确!
核心观点:
"因为存储桶/系统/数据库都是在同一台机器上的,所以所有流量只需要走一次穿透就行(老素材除外)?而现在的一些素材都是服务器走穿透访问的服务器本身的资源(老素材除外)?"
答案:
- ✅ 前半句正确:理论上,外网用户访问时只需要一次FRP穿透
- ✅ 后半句正确:当前配置下,服务器访问自己的MinIO也走FRP穿透,这是完全浪费的
详细说明
✅ 应该只需要一次FRP的场景(新素材)
| 场景 | 应该的流量路径 | FRP次数 |
|---|---|---|
| 外网用户访问预览图 | 浏览器 → FRP → MinIO | 1次 ✅ |
| 外网用户访问视频预览 | 浏览器 → FRP → MinIO | 1次 ✅ |
| 外网用户下载本地文件 | 浏览器 → FRP → 应用服务器 → 本地文件 | 1次 ✅ |
| 服务器上传到S3 | 服务器 → 内网直连 → MinIO | 0次 ✅ |
| 下载API从S3获取 | 浏览器 → FRP → 服务器 → 内网直连 → MinIO | 1次 ✅ |
❌ 当前实际发生的浪费场景
| 场景 | 当前流量路径 | FRP次数 | 问题 |
|---|---|---|---|
| 服务器上传到S3 | 服务器 → FRP → 自己的MinIO | 1次 ❌ | 浪费!应该内网直连 |
| 下载API从S3获取 | 浏览器 → FRP → 服务器 → FRP → 自己的MinIO | 2次 ❌ | 双重FRP!应该内网直连 |
| 产品全案下载 | 浏览器 → FRP → 服务器 → FRP×N → MinIO | 1+N次 ❌ | 每个文件都双重FRP |
优化后的理想状态
修改 S3_ENDPOINT=http://192.168.58.20:9000 后:
| 场景 | 优化后流量路径 | FRP次数 |
|---|---|---|
| 服务器上传到S3 | 服务器 → 内网直连 → MinIO | 0次 ✅ |
| 下载API从S3获取 | 浏览器 → FRP → 服务器 → 内网直连 → MinIO | 1次 ✅ |
| 产品全案下载 | 浏览器 → FRP → 服务器 → 内网直连×N → MinIO | 1次 ✅ |
结论:
- ✅ 所有新素材的流量都只需要一次FRP穿透(外网用户访问时)
- ✅ 服务器访问自己的资源应该不走FRP(内网直连)
- ✅ 老素材存储在外部S3,需要走FRP是正常的
文档版本: v1.1
最后更新: 2026-01-09
维护者: AI Assistant