我在选镜像站的时候,总会遇到一个矛盾:镜像站访问快、镜像站和上游同步延迟低(同步到了最新的包)两者不可兼得。
比较容易想到的解决思路是:只从同步延迟低的镜像下 db,然后从速度快的镜像开始依次试,跳过 404 的镜像,直到找到一个已经存在该文件的镜像。
在过往的十来年里,我一直是通过写一个脚本来分别给 pacman -Sy 和 pacman -Su 设置不同的镜像来勉强解决的。但是这个用法在 pacman 最新系列中被破坏了——pacman 加入了一个镜像站如果 404 次数过多,在同一次更新中就再也不尝试了的新行为。
想到以往的用法会在命令中夹杂许多 404 报错,需要专门的脚本来换镜像体验也并不是很好,我写了个非常简单的本地服务来实现这个需求:
#!/usr/bin/ruby
#
# A simple local redirector for pacman, to get you the latest packages and
# utilize available mirrors.
#
# Usage:
# - Set multiple mirrors in /etc/pacman.d/mirrorlist-accel with ordering:
# https://fastest-mirror-but-updates-once-a-day/archlinux/
# https://relatively-slower-mirror-that-updates-more-frequently/archlinux/
# ...
# https://pkgbuild-dot-com-or-another-mirror-that-gives-you-the-latest-packages/
#
# - Set /etc/pacman.d/mirrorlist to this redirector:
# Server = http://127.0.0.1:4567/$repo/os/$arch
require 'http'
require 'sinatra'
mirrors = File.readlines("/etc/pacman.d/mirrorlist-accel").filter_map { |line| line.strip if line && line[0] != "#" }
get '/*' do |path|
# Set TIER 0/1 mirrors as the last one, for:
# - DB syncing
# - Download fallback
# These two use cases always the same server for consistency.
mirror = mirrors[-1]
unless path.end_with? '.db'
# Find a faster mirror with the requested file present
mirrors[..-2].each { |m|
response = HTTP.head(m + path)
if response.status == 200
mirror = m
break
else
logger.info "skipping #{m} for #{path}, code: #{response.status}"
end
}
end
logger.info "redirecting to #{mirror + path}"
redirect mirror + path, 302
end
set :bind, ENV.fetch("PACMAN_ACCEL_BIND", "127.0.0.1")
set :port, ENV.fetch("PACMAN_ACCEL_PORT", "4567")
如注释所说,在 /etc/pacman.d/mirrorlist-accel 里按照本地访问速度依次设置几个快的镜像,并把最后一个镜像设置为和上游同步频繁的镜像即可。
贴上我目前使用的配置供参考:
$ cat /etc/pacman.d/mirrorlist-accel
https://mirrors.bfsu.edu.cn/archlinux/
https://mirrors.wsyu.edu.cn/archlinux/
https://geo.mirror.pkgbuild.com/
$ cat /etc/pacman.d/mirrorlist
Server = http://127.0.0.1:4567/$repo/os/$arch
原理很简单:pacman 访问本地 HTTP 服务,这个服务对非 .db 的下载请求按照配置依次 HTTP HEAD 直到找到一个返回值为 200 的镜像,然后返回 302 让 pacman 从这个镜像下载。
由于 .db 文件全部由最后一个镜像提供,而前面配置的镜像不存在对应文件时最终也会 fallback 到最后一个镜像,这样使用应该不会产生新的 db 不一致问题。
这个简单的工具日后会在我的小工具集 GitHub 仓库继续维护,同时我也创建了一个简单的 AUR 包:https://aur.archlinux.org/packages/pacman-accel-git
话说这个工具有没有可能支持 archlinuxcn 源~
是个问题……大概需要自定义-accel文件位置(