我在选镜像站的时候,总会遇到一个矛盾:镜像站访问快、镜像站和上游同步延迟低(同步到了最新的包)两者不可兼得。
比较容易想到的解决思路是:只从同步延迟低的镜像下 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 里按照本地访问速度依次设置几个快的镜像,并把最后一个镜像设置为和上游同步频繁的镜像即可。
Continue reading 用 pacman-accel 给 pacman 加速