#download #mirror #http #syncing #oss #overhead #lower

app tsumugu

一个具有较低开销的HTTP(S)同步工具,用于OSS镜像

5个不稳定版本

0.20240826.0 2024年8月26日
0.20240825.2 2024年8月25日
0.20240825.1 2024年8月25日
0.20240825.0 2024年8月24日
0.20240824.0 2024年8月24日

#452 in 网络编程

Download history 219/week @ 2024-08-18

每月219次下载

MIT许可协议

145KB
3.5K SLoC

tsumugu

一个具有较低开销的HTTP(S)同步工具,用于OSS镜像。

与逐个文件执行HEAD操作不同,tsumugu解析目录列表HTML,仅下载似乎未更新的文件。

设计目标

成功从这些域同步,其中lftp/rclone失败或遇到困难

待办事项

  • 添加"--include": 即使文件被--exclude正则表达式排除,也要同步。
  • 将支持的Debian、Ubuntu、Fedora和RHEL版本添加到--include正则表达式。
    • 类似于--include debian/${DEBIAN_VERSIONS}
  • 检查APT/YUM仓库的完整性(避免保留旧的无效元数据文件)
    • (此为实验性,可能工作不正常)

用法

> cargo run -- --help
    Finished dev [unoptimized + debuginfo] target(s) in 0.06s
     Running `target/debug/tsumugu --help`
A HTTP(S) syncing tool with lower overhead, for OSS mirrors

Usage: tsumugu <COMMAND>

Commands:
  sync  Sync files from upstream to local
  list  List files from upstream
  help  Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version
> cargo run -- sync --help
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
     Running `target/debug/tsumugu sync --help`
Usage: tsumugu sync [OPTIONS] <UPSTREAM> <LOCAL>

Arguments:
  <UPSTREAM>  The upstream URL
  <LOCAL>     The local directory

Options:
      --user-agent <USER_AGENT>
          Customize tsumugu's user agent [default: tsumugu]
      --dry-run
          Do not download files and cleanup
      --threads <THREADS>
          Threads at work [default: 2]
      --no-delete
          Do not clean up after sync
      --max-delete <MAX_DELETE>
          Set max delete count [default: 100]
      --timezone-file <TIMEZONE_FILE>
          You can set a valid URL for guessing. Set it to "no" to disable this behavior. By default it would recursivelly find the first file to HEAD for guessing
      --timezone <TIMEZONE>
          Manually set timezone (+- hrs). This overrides timezone_file
      --retry <RETRY>
          Retry count for each request [default: 3]
      --head-before-get
          Do an HEAD before actual GET. Otherwise when head-before-get and allow-time-from-parser are not set, when GETting tsumugu would try checking if we still need to download it
      --parser <PARSER>
          Choose a parser [default: nginx] [possible values: nginx, apache-f2, docker, directory-lister, lighttpd, caddy]
      --exclude <EXCLUDE>
          Excluded file regex. Supports multiple
      --include <INCLUDE>
          Included file regex (when it startswith any exclude regexes). Supports multiple
      --skip-if-exists <SKIP_IF_EXISTS>
          Skip file regex if they exist. Supports multiple
      --compare-size-only <COMPARE_SIZE_ONLY>
          File regex for those compare size only in HEAD requests. This only works with head_before_get
      --allow-mtime-from-parser
          Allow mtime from parser if not available from HTTP headers
      --apt-packages
          (Experimental) APT Packages file parser to find out missing packages
      --yum-packages
          (Experimental) YUM Packages file parser to find out missing packages
  -h, --help
          Print help
  -V, --version
          Print version
> cargo run -- list --help
    Finished dev [unoptimized + debuginfo] target(s) in 0.06s
     Running `target/debug/tsumugu list --help`
Usage: tsumugu list [OPTIONS] <UPSTREAM>

Arguments:
  <UPSTREAM>  The upstream URL

Options:
      --user-agent <USER_AGENT>        Customize tsumugu's user agent [default: tsumugu]
      --parser <PARSER>                Choose a parser [default: nginx] [possible values: nginx, apache-f2, docker, directory-lister, lighttpd, caddy, fancy-index, gradle]
      --exclude <EXCLUDE>              Excluded file regex. Supports multiple
      --include <INCLUDE>              Included file regex (even if excluded). Supports multiple
      --upstream-base <UPSTREAM_BASE>  The upstream base ending with "/" [default: /]
  -h, --help                           Print help
  -V, --version                        Print version

有关解析器的简要介绍,请参阅./src/parser/README.md

退出代码

  • 0: 成功
  • 1: 列表失败
  • 2: 下载失败
  • 3: 发生panic!()错误
  • 4: 清理时出错
  • 25: 限制停止删除

使用musl构建

遗憾的是,这需要openssl-sys,它不包括在交叉的预构建镜像中。尝试https://github.com/clux/muslrust

评估

默认并发为2个线程。

http://download.proxmox.com/

Proxmox使用自托管的CDN服务器架构,遗憾的是,其服务器将并发限制为仅1个(根据我测试的情况)。使用传统的lftp/rclone,同步可能需要>10小时(即使本地文件与远程文件相同)。

注意:如果您只需要其APT仓库,请考虑使用Proxmox离线镜像或其他工具,如apt-mirror

> time ./tsumugu sync --threads 1 --dry-run --exclude '^temp' http://download.proxmox.com/ /srv/repo/proxmox/
...

real	1m48.746s
user	0m3.468s
sys	0m3.385s

https://download.docker.com/

我们之前使用一个特殊的脚本来同步docker-ce,但现在tsumugu也可以处理这个。此外,对于linux/centos/和linux/rhel/中的30x,tsumugu可以创建链接,就像这个脚本之前做的那样。

> time ./tsumugu sync --timezone-file https://download.docker.com/linux/centos/docker-ce-staging.repo --parser docker --dry-run https://download.docker.com/ /srv/repo/docker-ce/
...

real	8m32.674s
user	0m4.532s
sys	0m2.855s

https://dl.winehq.org/wine-builds/

lftp/rclone 无法处理复杂的HTML。

> time ./tsumugu sync --parser apache-f2 --dry-run --exclude '^mageia' --exclude '^macosx' --exclude '^debian' --exclude '^ubuntu' --exclude '^fedora' --include '^debian/dists/${DEBIAN_CURRENT}' --include '^ubuntu/dists/${UBUNTU_LTS}' --include '^fedora/${FEDORA_CURRENT}' https://dl.winehq.org/wine-builds/ /srv/repo/wine/wine-builds/
...

<TIMESTAMP>  INFO ThreadId(01) tsumugu: (Estimated) Total objects: 17514, total size: 342.28 GiB

real	0m5.664s
user	0m1.475s
sys	0m0.294s

注意

Yuki集成

请参阅https://github.com/ustclug/ustcmirror-images#tsumugu

YAML示例

envs:
  UPSTREAM: http://download.proxmox.com/
  # tsumugu is not in yuki supported upstream image yet, so this is a workaround to correctly display the upstream URL
  $UPSTREAM: http://download.proxmox.com/
  TSUMUGU_EXCLUDE: --exclude ^temp --exclude pmg/dists/.+changelog$ --exclude devel/dists/.+changelog$
  TSUMUGU_TIMEZONEFILE: http://download.proxmox.com/images/aplinfo.dat
  TSUMUGU_THREADS: 1
image: ustcmirror/tsumugu:latest
interval: 12 3 * * *
logRotCycle: 10
name: proxmox
storageDir: /srv/repo/proxmox/

更多示例在examples/

正则表达式变量

请参阅./src/regex_process.rs

排除和包含

目前tsumugu使用一个简单的算法来确定路径是否应该完全排除、部分排除或包含

  1. 在解析正则表达式时,将变量(如${UBUNTU_LTS}等)替换为(?<distro_ver>.+)
  2. 首先,预处理用户的排除和包含。对于所有排除,如果它是任何包含的前缀,则将其放入list_only_regexes,否则将其放入instant_stop_regexes
  3. 当工作线程处理列出请求时,它们将首先检查是否匹配任何instant_stop_regexes。如果不匹配,则检查路径是否匹配任何包含正则表达式。如果匹配,则路径将被完全排除。然后,将路径与"rev_inner"正则表达式进行匹配,如果匹配则完全排除(一个快速的快捷方式)。最后,如果路径匹配list_only_regexes,则忽略此目录下的文件,但仍然列出子目录。不匹配任何正则表达式的路径将按常规包含。

在此过程中,一些不必要的路径仍然会被列出。将其视为简单性和性能之间的权衡。

此外,请注意,在生成用于比较的相对路径时使用这种逻辑

// Before working on one task:
let relative = task.relative.join("/");
// Before downloading a file:
let relative_filepath = PathBuf::from(&task_context.relative).join(&item.name);
let relative_filepath = relative_filepath.to_string_lossy();

item.name,或task.relative内的字符串,没有尾部斜杠,因此如果您想完全排除目录,则不应在正则表达式中放置尾部斜杠。

您可能会看到类似--exclude debian/ --include debian/dists/${DEBIAN_CURRENT}的参数,其中在示例中使用了尾部斜杠排除。这仅仅是因为我们不需要排除debian文件夹的外部目录列表。

去重

tsumugu依赖于本地文件大小和mtime来检查是否应该下载文件。一些文件级别的去重程序(如jdupes)在用硬链接去重时将忽略文件mtime。这对于某些仓库来说可能是一个问题,因为一些文件会反复重新下载,因为它们在本地没有正确的mtime。

解决方案

  • 设置--compare-size-only
  • 使用文件系统级别/块级别去重,如zfs dedup
  • 使用另一个考虑mtime的文件级别去重程序(尽管我不知道哪个会这样做)。

致谢

特别感谢NJU镜像的大量测试和错误报告。

命名

"tsumugu"这个名字,以及当前分支名"pudding",源自漫画《漂流少女与高贵之月》。

还有... tsumugu, drawn as simplified version of hitori

以非常简化的Hitori版本出现的tsumugu(显然我不太擅长绘画)。

旧(2020年),未完成的golang版本命名为"traverse",位于main-old分支下。

依赖

~31-46MB
~827K SLoC