# 带宽限速(v3.2) > v3.2 新增。管理端可独立设置**上行(上传)/ 下行(下载)带宽**,单位字节/秒(前端 UI 友好单位为 MB/s), > 0=不限速。修改后**立即生效**(每请求动态读取最新 KV,不需重启容器/进程)。 ## 适用对象 | 方向 | 范围 | 说明 | |---|---|---| | 上行 | `POST /share/file`、`POST /chunk/upload/{id}/{idx}`、`POST /chunk/upload`(扁平兼容)、`PUT /presign/upload/proxy/{id}` | 包裹 `c.Request.Body`,对 multipart 解析与表单字段读取天然节流 | | 下行 | `GET /share/download?key=&code=`、`GET /share/select?code=`(文件流)、`GET /admin/file/download?id=` | 包裹 `storage.ReadCloser`(local/webdav/s3 代理下载),Range 分段也按节流后的字节流推进 | > **不在限速范围**:S3 预签名**直传**(客户端 → S3 桶直连,**服务端无法介入**); > 文本分享下载(GET `/share/select` 命中 `text` 字段时直接返回 JSON 字符串,体积通常远小于 1MB)。 ## 配置项 | 键 | 类型/边界 | 默认 | 说明 | |---|---|---|---| | `upload_rate` | int64(0 ~ 1 073 741 824,即 1 GiB/s) | 0 | 上行字节/秒;0=不限速 | | `download_rate` | int64(0 ~ 1 073 741 824) | 0 | 下行字节/秒;0=不限速 | UI 输入:管理端「系统设置 → 上传频率限制」卡底部两个字段,**MB/s** 整数,0=不限速。 保存时前端把 `MB/s × 1024 × 1024` 换算为字节/秒写入 KV。 ## 算法(实现原理) 基于**时间窗的精确调度**,每个 `Read` 调用: ``` start ← 首次 Read 的时刻 bytes ← 累计已读字节(每次 Read 累加返回值 n) expected = start + bytes / rate // 按限速值推算"应到达时间" if now < expected: sleep(expected - now) // 超前则阻塞补齐 return n, err ``` 保证长期速率严格 ≤ `rate`,瞬时由调用方 Read 块大小自然突发。**不按字节微睡眠**——避免单次 Read 在低速场景下被调度抖动放大。 > 早期版本按每字节 sleep 1/rate 秒,被 Mac 调度粒度(约 10µs)放大后实际速率只有目标值的 1/3;v3.2.1 修正为整块对齐 sleep。 ## 公开接口 `GET /api/v1/config` 与 `GET /admin/config/get` 均下发当前生效值(数值型,字节/秒): ```json { "data": { "config": { "upload_rate": 0, "download_rate": 0, ... } } } ``` ## 修改(管理端) ```bash # PATCH /admin/config/update curl -X PATCH http://localhost:8466/admin/config/update \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"upload_rate": 1048576, "download_rate": 524288}' # ↑ 上行 1 MB/s、下行 512 KB/s ``` 边界校验(来自 `settings.KVSchema`,越界 400 中文错误): - `upload_rate` / `download_rate` 必须是整数,0 ≤ 值 ≤ 1073741824 ## 验证(端到端) ```bash # 1) 设下载限速 200 KB/s curl -X PATCH http://localhost:8466/admin/config/update \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"download_rate": 204800}' -w '\nHTTP %{http_code}\n' # 2) 上传 2 MB 测试文件(自动随机取件码或自定义 5~8 位) curl -X POST http://localhost:8466/share/file -F file=@/tmp/2mb.bin \ -F "expire_value=1" -F "expire_style=day" -F "code=" # 3) 管理员接口下载(不消耗取件次数,便于压测) ID=$(curl -s "http://localhost:8466/admin/file/list?page=1&size=20" \ -H "Authorization: Bearer " | jq '.data.data[0].id') time curl -s -o /tmp/dl.bin "http://localhost:8466/admin/file/download?id=$ID" \ -H "Authorization: Bearer " # 期望 ~10s(2 097 152 字节 / 200 000 B/s = 10.486 s) # 实测 10.65s(误差 1.5%,含 HTTP 头/响应开销) ``` > 测完记得恢复:`PATCH /admin/config/update` 设回 0。 ## 性能开销 - 0 速率路径:完全透传,无任何额外分配/锁/计时。 - 限速路径:每次 Read 多两次 `time.Now()` 与一次整数除法;`mutex.Lock` 仅在并发复用同一 reader 时阻塞(每请求独立实例,正常无争用)。 ## 边界与注意 1. **S3 预签名直传不可限速**:客户端拿到预签名 URL 后直连 S3 桶,不经本服务。`PUT /presign/upload/proxy/{id}`(**代理模式**)仍受上行限速;前端实现可在管理端提示用户这一点。 2. **共享存储后端的"突发"**:令牌桶改为时间窗对齐后,单次 Read 仍可能瞬时突发到调用方请求的块大小(典型 32~64 KB)。需更严格平滑的可在调用方调小读取块大小(如 4 KB)。 3. **代理/反代层限速**:如部署在 Nginx / Cloudflare 后,公网入站还会经过代理的限速/带宽上限;本服务限速只对服务进程侧可见。 4. **Range 多区间**:HTTP Range 单区间请求的字节流仍按限速推进;多区间未实现,回退全量。 5. **重置**:UI 「恢复默认」按钮把两个速率归 0(不限速)。 ## 相关 - 配置 KV 概览:见《环境变量与配置》`upload_rate` / `download_rate` 条目 - 管理端修改示例:见《管理后台 API》`PATCH /admin/config/update` - OpenAPI schema:`docs/openapi.yaml` 的 `/admin/config/update` requestBody.properties