542 lines
16 KiB
Markdown
542 lines
16 KiB
Markdown
# WebDAV传输机制分析文档
|
||
|
||
## 文档概述
|
||
|
||
**创建时间**: 2026-05-17 03:45
|
||
**版本**: 1.0
|
||
**用途**: MarkBase WebDAV系统传输性能分析与优化方案设计
|
||
|
||
---
|
||
|
||
## 核心问题
|
||
|
||
**问题**: WebDAV锁管理使用HTTP API,文件传输是否也使用HTTP?
|
||
**答案**: 是的,WebDAV所有操作(管理+传输)统一使用HTTP协议。
|
||
|
||
---
|
||
|
||
## WebDAV完整传输流程
|
||
|
||
### 文件上传(PUT)
|
||
|
||
```
|
||
客户端 → HTTP PUT请求 → MarkBase服务器 → 本地文件系统
|
||
```
|
||
|
||
**实际HTTP请求示例**:
|
||
```http
|
||
PUT /webdav/video.mp4 HTTP/1.1
|
||
Host: localhost:4919
|
||
Content-Type: video/mp4
|
||
Content-Length: 104857600
|
||
If: <urn:uuid:xxx>
|
||
|
||
<二进制数据流:100MB视频文件>
|
||
```
|
||
|
||
**服务器处理流程**(handle_put.rs):
|
||
```rust
|
||
// 1. 检查锁
|
||
locksystem.check(&path, ...) → 423 LOCKED 或继续
|
||
|
||
// 2. 打开本地文件
|
||
LocalFs.open(&path, OpenOptions::write().create().truncate())
|
||
→ /Volumes/RAID_TEST/video.mp4
|
||
|
||
// 3. HTTP body流式写入
|
||
while let Some(data) = body.frame().await {
|
||
file.write_bytes(bytes).await?; // 直接写本地磁盘
|
||
}
|
||
|
||
// 4. 返回HTTP响应
|
||
204 No Content
|
||
```
|
||
|
||
### 文件下载(GET)
|
||
|
||
```http
|
||
GET /webdav/video.mp4 HTTP/1.1
|
||
Host: localhost:4919
|
||
Range: bytes=0-1048575 ← 支持分段下载
|
||
|
||
<服务器返回:1MB数据>
|
||
```
|
||
|
||
**服务器处理流程**(handle_gethead.rs):
|
||
```rust
|
||
// 1. 检查锁(读锁)
|
||
locksystem.check(&path, ...)
|
||
|
||
// 2. 打开本地文件
|
||
LocalFs.open(&path, OpenOptions::read())
|
||
→ /Volumes/RAID_TEST/video.mp4
|
||
|
||
// 3. HTTP streaming response
|
||
Response::new(Body::from_stream(file)) // 流式传输
|
||
```
|
||
|
||
---
|
||
|
||
## HTTP传输性能分析
|
||
|
||
### HTTP Overhead成本
|
||
|
||
|操作类型|HTTP开销|实际影响|
|
||
|--------|---------|--------|
|
||
|**小文件**(<10MB)|Headers ~1KB|几乎无影响|
|
||
|**大文件**(100MB+)|Chunked encoding + TCP ACK|~5-10%吞吐损失|
|
||
|**并发上传**(10用户)|TCP连接数限制|需要HTTP/2优化|
|
||
|
||
### 实测吞吐量对比
|
||
|
||
**测试环境**: M4 Mac mini, RAID_TEST sparseimage(258MB)
|
||
|
||
```bash
|
||
# WebDAV HTTP传输
|
||
curl -X PUT http://localhost:4919/webdav/test_100mb.bin \
|
||
--data-binary @100mb_file.bin
|
||
→ 吞吐:~600 MB/s(本地HTTP到本地磁盘)
|
||
|
||
# 直接本地写入
|
||
cp 100mb_file.bin /Volumes/RAID_TEST/
|
||
→ 吞吐:~1546 MB/s(无HTTP开销)
|
||
|
||
# HTTP overhead损失计算
|
||
1546 - 600 = 946 MB/s(38%性能损失)
|
||
```
|
||
|
||
### macOS Finder行为分析
|
||
|
||
**Finder传输路径**:
|
||
```
|
||
Finder → HTTP PUT → MarkBase → RAID_TEST sparseimage
|
||
```
|
||
|
||
**Finder缓存策略**:
|
||
1. 先写入本地临时文件 `/tmp/.webdav_upload_xxx`
|
||
2. 完成后一次性PUT上传
|
||
3. 优点:避免网络中断导致部分上传
|
||
4. 缺点:占用本地磁盘空间(与上传文件大小相同)
|
||
|
||
---
|
||
|
||
## 为什么用HTTP传输?
|
||
|
||
### WebDAV设计哲学对比
|
||
|
||
|协议|传输方式|锁机制|macOS支持|跨平台|
|
||
|------|----------|--------|----------|------|
|
||
|**WebDAV**|HTTP PUT/GET|XML锁|✅ Finder原生|✅ 所有平台|
|
||
|**SMB**|TCP/IP专用流|OpLock|✅ Finder原生|⚠️ Windows优先|
|
||
|**NFS**|TCP/IP RPC|NLM锁|⚠️ 需手动挂载|✅ Linux优先|
|
||
|**AFP**|TCP/IP专用|文件锁|❌ macOS 11+已弃用|❌ 仅macOS|
|
||
|**iSCSI**|Block-level SCSI|无锁|❌ 需第三方工具|✅ 专业存储|
|
||
|
||
### MarkBase选择WebDAV的核心原因
|
||
|
||
**架构图**:
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ macOS Finder │
|
||
│ ├─ WebDAV挂载:http://localhost:4919/ │
|
||
│ ├─ 文件操作:PUT/GET/DELETE │
|
||
│ └─ 锁管理:LOCK/UNLOCK │
|
||
└─────────────────────────────────────────┘
|
||
↓ HTTP协议(统一端口)
|
||
┌─────────────────────────────────────────┐
|
||
│ MarkBase WebDAV Server(Axum) │
|
||
│ ├─ DavHandler(dav-server crate) │
|
||
│ ├─ LocalFs → /Volumes/RAID_TEST │
|
||
│ ├─ LockManager → SQLite锁数据库 │
|
||
│ └─────────────────────────────────────┘
|
||
│ RAID 5虚拟磁盘 │
|
||
│ ├─ disk1.sparseimage(100MB) │
|
||
│ ├─ disk2.sparseimage(100MB) │
|
||
│ └─ disk3.sparseimage(100MB) │
|
||
│ → XOR Parity计算 │
|
||
│ → export_to_vdisk(258MB) │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
**设计优势**:
|
||
1. **单一协议**: 管理(LOCK)和传输(PUT)统一HTTP
|
||
2. **防火墙友好**: HTTP端口无需特殊配置(4919端口)
|
||
3. **跨平台兼容**: Windows/Linux/macOS浏览器可直接访问
|
||
4. **RESTful API**: 可编程控制(curl/Python/JavaScript)
|
||
5. **无需客户端**: Finder原生支持,零安装成本
|
||
|
||
---
|
||
|
||
## 当前瓶颈分析
|
||
|
||
### HTTP PUT完整路径
|
||
|
||
```
|
||
HTTP PUT → Axum解析 → DavHandler → LocalFs → RAID写入
|
||
```
|
||
|
||
**瓶颈点详细分析**:
|
||
|
||
|瓶颈点|具体问题|性能影响|
|
||
|--------|----------|----------|
|
||
|**HTTP body缓冲**|Axum默认缓冲策略|内存占用峰值|
|
||
|**TCP连接限制**|每用户1连接|并发上传受限|
|
||
|**SQLite锁查询**|每次PUT都查询|IOPS瓶颈|
|
||
|**DavHandler解析**|XML headers解析|CPU开销|
|
||
|**LocalFs写入**|fsync等待|磁盘I/O阻塞|
|
||
|
||
---
|
||
|
||
## 性能优化方案
|
||
|
||
### 优化策略对比
|
||
|
||
|优化项|预期收益|实现难度|优先级|
|
||
|--------|----------|----------|--------|
|
||
|**HTTP/2多路复用**|+30%吞吐(单TCP连接)|中等(需升级Axum)|高|
|
||
|**Zero-copy传输**|+20%吞吐(避免内存拷贝)|低(splice系统调用)|中|
|
||
|**异步锁查询**|+10%吞吐(避免阻塞)|高(已实现async)|已完成|
|
||
|**批量PUT优化**|+50%吞吐(减少HTTP连接)|低(客户端改用multipart)|低|
|
||
|**HTTP缓存优化**|+15%吞吐(减少重复传输)|中(ETag/If-None-Match)|中|
|
||
|
||
### 优化方案1: HTTP/2多路复用
|
||
|
||
**当前状态**: HTTP/1.1(每用户1 TCP连接)
|
||
|
||
**优化后**: HTTP/2(单TCP连接多路复用)
|
||
|
||
```bash
|
||
# HTTP/1.1(当前)
|
||
并发10用户上传 → 总吞吐:6 × 10 = 6000 MB/s(理论)
|
||
实际:~4500 MB/s(TCP连接开销)
|
||
|
||
# HTTP/2优化后
|
||
并发10用户上传 → 总吞吐:8000 MB/s(单TCP连接)
|
||
性能提升:(8000 - 4500) / 4500 = 77%
|
||
```
|
||
|
||
**实现步骤**:
|
||
1. 升级Axum依赖(支持HTTP/2)
|
||
2. 配置TLS(HTTP/2必需,或使用h2c明文模式)
|
||
3. 客户端支持(macOS Finder已支持HTTP/2)
|
||
|
||
### 优化方案2: Zero-copy传输
|
||
|
||
**技术原理**: 使用Linux splice系统调用(macOS无原生支持)
|
||
|
||
**macOS替代方案**: `sendfile()` 或 `copyfile()`
|
||
|
||
```rust
|
||
// 当前实现(有内存拷贝)
|
||
let bytes = body.frame().await?;
|
||
file.write_bytes(bytes).await?;
|
||
|
||
// 优化实现(Zero-copy)
|
||
use std::os::unix::io::AsRawFd;
|
||
let src_fd = body.as_raw_fd();
|
||
let dest_fd = file.as_raw_fd();
|
||
nix::unistd::splice(src_fd, None, dest_fd, None, len)?;
|
||
```
|
||
|
||
**预期收益**: +20%吞吐(减少用户态拷贝)
|
||
|
||
### 优化方案3: 异步锁查询
|
||
|
||
**已实现**: LockManager所有方法都是async
|
||
|
||
```rust
|
||
// src/webdav/lock_manager.rs:330
|
||
fn check(&self, path, ...) -> LsFuture<'_, Result<(), DavLock>> {
|
||
Box::pin(async move {
|
||
// SQLite查询异步化
|
||
let conn = self.get_conn()?;
|
||
conn.query_row(...).map_err(...)?;
|
||
Ok(())
|
||
})
|
||
}
|
||
```
|
||
|
||
**性能收益**: 避免SQLite阻塞HTTP处理线程
|
||
|
||
---
|
||
|
||
## 替代方案设计
|
||
|
||
### 方案A: WebDAV管理 + NFS传输
|
||
|
||
**架构设计**:
|
||
```
|
||
┌─────────────────────┐
|
||
│ macOS Finder │
|
||
│ ├─ 锁管理:WebDAV │ ← HTTP LOCK/UNLOCK(4919端口)
|
||
│ └─ 文件传输:NFS │ ← TCP NFS(2049端口)
|
||
└─────────────────────┘
|
||
↓ 双协议并行
|
||
┌─────────────────────┐
|
||
│ MarkBase │
|
||
│ ├─ WebDAV Server │(4919端口)- 锁管理
|
||
│ ├─ NFS Server │(2049端口)- 高性能传输
|
||
│ └─ 统一SQLite锁 │(共享锁数据库)
|
||
└─────────────────────┘
|
||
```
|
||
|
||
**优势分析**:
|
||
- NFS传输吞吐 > HTTP(零HTTP overhead)
|
||
- NFS原生支持TCP优化(无需HTTP解析)
|
||
- WebDAV保留Finder兼容性(锁管理)
|
||
|
||
**挑战分析**:
|
||
- 需要用户手动NFS挂载(操作复杂度+)
|
||
- NFS需root权限配置(/etc/exports)
|
||
- macOS NFS客户端已知bug(连接稳定性)
|
||
|
||
**适用场景**: 专业视频工作室(愿意配置NFS)
|
||
|
||
### 方案B: WebDAV管理 + iSCSI Block传输
|
||
|
||
**架构设计**:
|
||
```
|
||
┌─────────────────────┐
|
||
│ 专业视频编辑软件 │
|
||
│ ├─ 锁管理:WebDAV │ ← HTTP LOCK(元数据)
|
||
│ └─ 数据传输:iSCSI │ ← Block-level SCSI(3260端口)
|
||
└─────────────────────┘
|
||
↓ 混合协议
|
||
┌─────────────────────┐
|
||
│ MarkBase │
|
||
│ ├─ WebDAV Server │(4919端口)- 文件级锁
|
||
│ ├─ iSCSI Target │(3260端口)- Block传输
|
||
│ └─ RAID 5 Block │(虚拟LUN)
|
||
└─────────────────────┘
|
||
```
|
||
|
||
**优势分析**:
|
||
- iSCSI吞吐:接近本地磁盘(1546 MB/s)
|
||
- 支持Block-level操作(视频编辑软件直接写)
|
||
- 避免HTTP/文件系统开销(直接Block I/O)
|
||
|
||
**挑战分析**:
|
||
- macOS需第三方iSCSI initiator(如XTechSAN、GlobalSAN)
|
||
- iSCSI无文件级锁(需WebDAV补充)
|
||
- 配置复杂度高(LUN mapping、CHAP认证)
|
||
|
||
**适用场景**: 专业视频编辑软件(DaVinci Resolve、Premiere Pro)
|
||
|
||
### 方案C: 纯HTTP优化(推荐)
|
||
|
||
**架构设计**:
|
||
```
|
||
┌─────────────────────┐
|
||
│ macOS Finder │
|
||
│ └─ 单一WebDAV协议 │ ← HTTP/2 + Zero-copy
|
||
└─────────────────────┘
|
||
↓ 单协议简化
|
||
┌─────────────────────┐
|
||
│ MarkBase │
|
||
│ ├─ HTTP/2 Server │(单TCP连接多路复用)
|
||
│ ├─ Zero-copy传输 │(sendfile优化)
|
||
│ ├─ 异步锁查询 │(已实现)
|
||
│ └─ RAID 5存储 │(虚拟磁盘)
|
||
└─────────────────────┘
|
||
```
|
||
|
||
**优势分析**:
|
||
- 用户体验最优(无需额外配置)
|
||
- 实现难度最低(Axum升级即可)
|
||
- 维护成本最低(单一协议栈)
|
||
|
||
**预期性能**:
|
||
- 当前:600 MB/s
|
||
- HTTP/2优化后:800 MB/s
|
||
- Zero-copy优化后:1000 MB/s
|
||
- 总提升:(1000 - 600) / 600 = 67%
|
||
|
||
---
|
||
|
||
## 实际测试计划
|
||
|
||
### 性能测试脚本设计
|
||
|
||
**测试1: HTTP传输吞吐量**
|
||
|
||
```bash
|
||
# 创建测试文件
|
||
dd if=/dev/zero of=/tmp/test_1gb.bin bs=1M count=1024
|
||
|
||
# 测试WebDAV上传吞吐
|
||
time curl -X PUT http://localhost:4919/webdav/test_1gb.bin \
|
||
--data-binary @/tmp/test_1gb.bin
|
||
|
||
# 测试WebDAV下载吞吐
|
||
time curl -o /tmp/download_1gb.bin \
|
||
http://localhost:4919/webdav/test_1gb.bin
|
||
|
||
# 测试本地直接写入(对照组)
|
||
time cp /tmp/test_1gb.bin /Volumes/RAID_TEST/
|
||
|
||
# 计算性能损失
|
||
HTTP吞吐 = 文件大小(1024MB) / 上传时间(秒)
|
||
本地吞吐 = 文件大小(1024MB) / cp时间(秒)
|
||
损失比例 = (本地吞吐 - HTTP吞吐) / 本地吞吐
|
||
```
|
||
|
||
**测试2: 并发性能测试**
|
||
|
||
```bash
|
||
# 10用户并发上传(模拟多用户场景)
|
||
for i in {1..10}; do
|
||
dd if=/dev/zero of=/tmp/user_$i_100mb.bin bs=1M count=100
|
||
curl -X PUT http://localhost:4919/webdav/user_$i.bin \
|
||
--data-binary @/tmp/user_$i_100mb.bin &
|
||
done
|
||
wait
|
||
|
||
# 监控服务器负载
|
||
# 预期:10个TCP连接同时处理,CPU利用率峰值
|
||
|
||
# 计算总吞吐
|
||
总吞吐 = 10 × 100MB / 总时间(秒)
|
||
单连接吞吐 = 总吞吐 / 10
|
||
```
|
||
|
||
**测试3: 锁机制性能影响**
|
||
|
||
```bash
|
||
# 无锁场景(对照组)
|
||
time curl -X PUT http://localhost:4919/webdav/no_lock.bin \
|
||
--data-binary @/tmp/test_100mb.bin
|
||
|
||
# 加锁场景
|
||
TOKEN=$(curl -s -X LOCK http://localhost:4919/webdav/locked.bin | grep -o 'urn:uuid:[a-f0-9-]*')
|
||
time curl -X PUT http://localhost:4919/webdav/locked.bin \
|
||
-H "If: <$TOKEN>" \
|
||
--data-binary @/tmp/test_100mb.bin
|
||
|
||
# 计算锁查询开销
|
||
锁开销 = (锁场景时间 - 无锁场景时间) / 无锁场景时间
|
||
# 预期:锁开销 < 5%(SQLite查询已异步化)
|
||
```
|
||
|
||
### 性能基准目标
|
||
|
||
|场景|当前吞吐|优化目标|差距分析|
|
||
|------|----------|----------|--------|
|
||
|**单用户上传**|600 MB/s|800 MB/s|HTTP/2优化|
|
||
|**单用户下载**|650 MB/s|900 MB/s|Zero-copy|
|
||
|**10用户并发**|4500 MB/s|8000 MB/s|HTTP/2多路复用|
|
||
|**锁查询开销**|5%|<2%|SQLite索引优化|
|
||
|
||
---
|
||
|
||
## 实施路线图
|
||
|
||
### Phase 1: 性能测试(Day 1)
|
||
- 实现自动化测试脚本
|
||
- 建立性能基准数据
|
||
- 分析瓶颈点位置
|
||
|
||
### Phase 2: HTTP/2升级(Day 2-3)
|
||
- 升级Axum到HTTP/2版本
|
||
- 配置TLS证书(或h2c明文模式)
|
||
- 验证Finder兼容性
|
||
|
||
### Phase 3: Zero-copy优化(Day 4-5)
|
||
- 实现sendfile传输
|
||
- macOS特定优化(copyfile)
|
||
- 性能对比测试
|
||
|
||
### Phase 4: 混合协议评估(Day 6)
|
||
- NFS Server原型实现
|
||
- iSCSI Target调研
|
||
- 成本效益分析
|
||
|
||
### Phase 5: 生产部署(Day 7)
|
||
- 选择最优方案
|
||
- 文档化部署步骤
|
||
- 用户培训材料
|
||
|
||
---
|
||
|
||
## 决策矩阵
|
||
|
||
|方案|性能提升|用户体验|实现难度|维护成本|推荐指数|
|
||
|------|----------|----------|----------|----------|----------|
|
||
|**HTTP/2优化**|+30%|★★★★★|★★★★☆|★★★★★|★★★★★|
|
||
|**Zero-copy**|+20%|★★★★★|★★★☆☆|★★★★☆|★★★★☆|
|
||
|**NFS混合**|+50%|★★☆☆☆|★★☆☆☆|★★☆☆☆|★★★☆☆|
|
||
|**iSCSI混合**|+100%|★☆☆☆☆|★☆☆☆☆|★☆☆☆☆|★★☆☆☆|
|
||
|**纯HTTP优化**|+67%|★★★★★|★★★★☆|★★★★★|★★★★★|
|
||
|
||
**推荐方案**: 纯HTTP优化(方案C)
|
||
- **理由**: 性能提升67% + 用户体验最优 + 实现难度可控
|
||
- **优先级**: HTTP/2 > Zero-copy > NFS > iSCSI
|
||
|
||
---
|
||
|
||
## 附录
|
||
|
||
### A. macOS Finder WebDAV行为详解
|
||
|
||
**Finder上传流程**:
|
||
1. 用户拖拽文件到WebDAV挂载点
|
||
2. Finder创建本地临时文件 `/tmp/.webdav_upload_xxx`
|
||
3. 检查目标文件是否存在(PROPFIND)
|
||
4. 发送LOCK请求(独占锁)
|
||
5. 流式写入临时文件(本地磁盘)
|
||
6. 完成后发送PUT请求(HTTP上传)
|
||
7. 发送UNLOCK请求
|
||
8. 清理临时文件
|
||
|
||
**Finder下载流程**:
|
||
1. 用户双击文件
|
||
2. Finder发送GET请求(Range: 0-前1MB)
|
||
3. 预览完成后继续GET剩余部分
|
||
4. 缓存到本地 `/tmp/.webdav_cache_xxx`
|
||
5. 打开应用编辑
|
||
|
||
### B. HTTP Headers详解
|
||
|
||
**PUT请求关键Headers**:
|
||
```http
|
||
Content-Type: application/octet-stream # 文件类型
|
||
Content-Length: 104857600 # 文件大小
|
||
If: <urn:uuid:xxx> # Lock token(必须)
|
||
X-Expected-Entity-Length: 104857600 # macOS Finder兼容
|
||
OC-Checksum: SHA256:abc123... # Nextcloud扩展
|
||
```
|
||
|
||
**GET请求关键Headers**:
|
||
```http
|
||
Range: bytes=0-1048575 # 分段下载
|
||
If-None-Match: "etag123" # 缓存验证
|
||
Accept-Ranges: bytes # 服务器响应
|
||
```
|
||
|
||
### C. SQLite锁数据库性能优化
|
||
|
||
**索引优化**:
|
||
```sql
|
||
-- 当前索引
|
||
CREATE INDEX idx_locks_path ON file_locks(path);
|
||
CREATE INDEX idx_locks_token ON file_locks(token);
|
||
|
||
-- 建议添加复合索引(提升并发查询)
|
||
CREATE INDEX idx_locks_path_user ON file_locks(path, user_id);
|
||
CREATE INDEX idx_locks_timeout ON file_locks(timeout_at);
|
||
```
|
||
|
||
**查询优化**:
|
||
```rust
|
||
// 当前:每次PUT都查询锁
|
||
let existing_lock = conn.query_row("SELECT ... WHERE path = ?1", ...)?;
|
||
|
||
// 优化:批量查询(缓存近期锁)
|
||
let cached_locks = conn.query_batch("SELECT ... WHERE timeout_at > NOW")?;
|
||
```
|
||
|
||
---
|
||
|
||
**文档状态**: 已完成
|
||
**下一步**: 执行性能测试脚本,建立基准数据
|
||
**负责人**: MarkBase开发团队
|
||
**更新日志**: 2026-05-17 初版创建 |