feat(download): 多线程下载 + 重试指数退避(issue #17)

下载主循环由串行改为「并发下载 + 串行簿记」三段式:
- Phase A:无网络前置校验(官方性/目标路径/建目录)+ 判定跳过/需下载,
  非官方 URL 在任何下载前 fail-fast
- Phase B:need-download 项经 scoped 线程池并发下载(默认并发 4,
  可配 1..=256)。worker 只做只读 &self 的 pull_one(各 URL 独立
  .part/目标文件),经 mpsc 把结果送回主线程;manifest/quarantine
  簿记与进度回调全在主线程串行执行,无需加锁。首个失败或 should_cancel
  置 cancel 标志,其余 worker 在任务边界停止
- Phase C:按 plan 顺序串行收尾——seed .hash 校验(顺序相关、可
  fail-fast)+ 构建有序结果

fail-fast 与「不发布不完整资源」不变量保留;进度事件按 URL 配对但
顺序不再单调(并发下天然如此)。

curl 重试加指数退避(网络类 200ms→400ms→800ms…上限 5s;ETXTBSY 仍走
极短退避),并发下对官方 CDN 更礼貌;退避基值 cfg(test) 下为 0 不拖慢
单测。

并发度经 OfficialUpdateConfig.download_concurrency 贯通,CLI
--download-concurrency 与 BAT_DOWNLOAD_CONCURRENCY 可配,.env 模板
与 USERGUIDE/CURRENT_STATUS/CHANGELOG 同步。

验证:新增并发正确性测试(并发 8:每 URL 恰一次 started+finished、
全部落盘)、并发度钳制、退避时长计算、CLI/env 解析单测;progress
排序测试改为顺序无关不变量;workspace 全测试(20 套件) + fmt +
clippy --all-targets -D warnings 全绿。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 03:25:04 -07:00
co-authored by Claude Fable 5
parent 8efd8f36b4
commit 0ab3f3b953
7 changed files with 449 additions and 78 deletions
+2 -1
View File
@@ -84,6 +84,7 @@ HTTPS_PROXY=http://user:pass@127.0.0.1:7890 bat --auto-discover --daemon
| `--proxy <URL\|auto\|none>` | curl 代理覆盖(默认 `auto`,从环境变量检测)。scheme 支持 http/https/socks4/socks4a/socks5/socks5h |
| `--no-proxy` | 强制直连 |
| `--unzip <PATH>` | unzip 可执行文件(默认 `unzip` |
| `--download-concurrency <N>` | 并行下载数,`1..=256`(默认 `4`)。并行仅作用于实际网络下载;manifest/quarantine 簿记与 seed `.hash` 校验仍串行,`fail-fast` 与「不发布不完整资源」不变量保留。可下载失败重试带指数退避以对官方 CDN 礼貌 |
| `--dry-run` | 不写同步状态 |
| `--plan` | dry-run 时输出计划中的 URL |
| `--force` | 强制下载/刷新 |
@@ -126,7 +127,7 @@ HTTPS_PROXY=http://user:pass@127.0.0.1:7890 bat --auto-discover --daemon
- 优先级:**命令行参数 > 进程环境变量 > `.env` > 内置默认值**。
- 语法:每行 `KEY=VALUE``#` 开头为注释;值两侧成对引号会剥除;空值视为未设置。
- 支持的键:`BAT_OUTPUT``BAT_STATE_DIR``BAT_AUTO_DISCOVER``BAT_WATCH``BAT_DAEMON``BAT_PROXY``BAT_NO_PROXY``BAT_INTERVAL_SECONDS``BAT_ERROR_RETRY_SECONDS``BAT_APP_VERSION``BAT_CONNECTION_GROUP``BAT_LAUNCHER_VERSION``BAT_PLATFORMS``BAT_CURL``BAT_UNZIP``BAT_JSON``BAT_QUIET_UP_TO_DATE`;也可以直接写 `HTTPS_PROXY` 等通用环境变量(走现有代理自动检测)。布尔值支持 `1/0/true/false/yes/no/on/off`
- 支持的键:`BAT_OUTPUT``BAT_STATE_DIR``BAT_AUTO_DISCOVER``BAT_WATCH``BAT_DAEMON``BAT_PROXY``BAT_NO_PROXY``BAT_INTERVAL_SECONDS``BAT_ERROR_RETRY_SECONDS``BAT_DOWNLOAD_CONCURRENCY``BAT_APP_VERSION``BAT_CONNECTION_GROUP``BAT_LAUNCHER_VERSION``BAT_PLATFORMS``BAT_CURL``BAT_UNZIP``BAT_JSON``BAT_QUIET_UP_TO_DATE`;也可以直接写 `HTTPS_PROXY` 等通用环境变量(走现有代理自动检测)。布尔值支持 `1/0/true/false/yes/no/on/off`
- `BAT_WATCH` / `BAT_DAEMON` 只对无子命令的 `bat` 生效(两者同时为 `1` 时 daemon 优先);命令行显式传入 `--watch` / `--daemon` / `--dry-run``.env` 的模式开关让位。`status` / `verify` 等子命令不受它们影响。
- `BAT_REDIS_URL` / `BAT_REDIS_PASSWORD` 为**预留键**:Redis 任务后端尚未接入,当前任务历史持久化在 `<state-dir>/bat-tasks.json`
-`BAT_SKIP_ENV_FILE=1` 可让 `bat` 完全跳过 `.env` 的生成与加载。