用 pacman-accel 给 pacman 加速

我在选镜像站的时候,总会遇到一个矛盾:镜像站访问快、镜像站和上游同步延迟低(同步到了最新的包)两者不可兼得。

比较容易想到的解决思路是:只从同步延迟低的镜像下 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

2 thoughts on “用 pacman-accel 给 pacman 加速”

Leave a Reply to hu3rror Cancel reply

Your email address will not be published. Required fields are marked *

QR Code Business Card