开源镜像站缓存加速

除了某些土豪站用全闪以外,大多数开源镜像站都是以机械盘为主,当带宽充裕的时候IO显然会成为瓶颈,那么采用少量的SSD来提升性能几乎是唯一的办法。加SSD的方法有很多种:

  1. lvmcache bcache bcachefs:因在用ZFS所以没有测试这类方案,看上去比较复杂;
  2. ZFS L2ARC:实际测试基本无效果,zpool iostat 看L2ARC读取很少;
  3. nginx proxy_cache:效果显著,但是必须用反代。

proxy_cache配置在前端nginx上,前端nginx访问SSD,前端nginx需要编译安装ngx_cache_purge以便主动清除缓存。前端nginx反代给后端nginx,后端nginx访问机械盘提供实际的数据。当然也可以一个nginx同时访问SSD和机械盘,这个nginx自己反代给自己,因为只有反代了才能缓存,前端nginx缓存配置如下:

http {
    ……
    proxy_cache_path /cache levels=1:2 use_temp_path=off keys_zone=mirror:64m inactive=24h max_size=2600g;
    ……
}
server {
    listen 80;
    listen [::]:80;
    listen 443 ssl;
    listen [::]:443 ssl;
    listen *:443 quic;
    listen [::]:443 quic;
    server_name mirror.nju.edu.cn mirrors.nju.edu.cn;
    ……
    proxy_cache mirror;
    proxy_cache_key $request_uri;
    proxy_cache_valid 200 24h;
    proxy_cache_valid 301 302 1h;
    proxy_cache_valid any 1m;
    proxy_cache_lock on;
    proxy_cache_lock_age 3s;
    proxy_cache_lock_timeout 3s;
    proxy_cache_use_stale error timeout updating;
    proxy_cache_background_update on;
    proxy_cache_revalidate on;
    cache_purge_response_type text;
    proxy_cache_purge PURGE from 127.0.0.1 192.168.10.10;
    add_header X-Cache-Status $upstream_cache_status;
    ……
    location / {
        proxy_pass http://192.168.10.10:8000;

        ……
    }
    ……
}
  • proxy_cache_path:
    use_temp_path=off 临时文件直接放到 cache 目录中,防止文件在 temp_path 和 cache_path 之间移动。缓存文件会先放在 temp_path 中,下载完了再移动到 cache_path 下,设定成 off 比较简单安全。
    keys_zone=mirror:64m 内存缓存区名称是 mirror 容量是64MB,每MB内存可以缓存八千个key,可以估算一下key的数量设定缓存区大小。
    inactive=6h 超过6小时没有访问的缓存会被删除,无论是否 valid,防止缓存被长期未用的数据占用。
    max_size=1946g 要比缓存目录 /cache 容量稍小,如果缓存用尽 nginx 会报错 [crit] …… failed (2: No such file or directory) 而且会直接断开客户端链接。
  • proxy_cache mirror:匹配前面 proxy_cache_path 的内存缓存区名称。
  • proxy_cache_key $request_uri:原始请求的URL,用这个URL用作缓存的key,只要这个key(请求URL)一样则nginx用缓存中一样的数据回复。
  • proxy_cache_valid 200 24h; proxy_cache_valid 301 302 1h; proxy_cache_valid any 1m;:HTTP状态码200的缓存24小时、301/302的缓存1小时、其它的缓存1分钟,缓存达到这个时间就会被抛弃,防止数据被长期缓存不能得到更新。
  • proxy_cache_lock on:当有多个请求同一个文件时(同一个key),只有第一个请求从反代中获取,通过加锁让后续的请求等待第一个请求完成后从缓存中获取,防止并发从后端中请求同一个文件,加重后端负载。
  • proxy_cache_lock_age 3s:第一个请求锁定3秒,到期后就释放锁,下一个请求发送到后端。
  • proxy_cache_lock_timeout 3s:后续的请求如果等待3秒还未从缓存中获取,就直接从后端获取,但是不缓存。
  • proxy_cache_use_stale error timeout updating:当请求后端时出现错误(error)或超时(timeout)时使用过期缓存,如果因缓存过期正在更新中(updating)时使用过期缓存,过期的缓存总比没有好。
  • proxy_cache_background_update on:允许后台请求过期的缓存。
  • proxy_cache_revalidate on:更新缓存时使用 If-Modified-Since 和 If-None-Match 请求后端,后端可以返回304而非200减少传输。
  • cache_purge_response_type text:清除缓存后返回文本格式的结果。
  • proxy_cache_purge PURGE from 127.0.0.1 192.168.10.10:使用PURGE这个HTTP方法清除缓存,允许本地和192.168.10.10来清除。
  • add_header X-Cache-Status $upstream_cache_status:添加HTTP头X-Cache-Status以便客户端知道缓存情况。

后端nginx可能需要设定某些文件的缓存有效期,比如提供给mirrorz的相关文件需要根据不同的请求站点设置不同的 Access-Control-Allow-Origin 头以实现跨域,但是URL相同则前端缓存key相同,前端无法根据不同的请求站点回复不同的HTTP头,在缓存有效期内均回复第一次访问缓存下来的HTTP头,所以比较简单的方法就是直接禁用有关文件的缓存:

server {
    listen 8000;
    root /mirror;
    ……
    location ~ ^/mirrorz/ {
        expires -1;
        ……
    }
    ……
}
  • expires -1:负值禁用缓存,通过添加HTTP头 Cache-Control: no-cache 和 Expires,Expires被设定为当前时间之前,这样前端nginx就不会缓存 mirrorz 的URL了。

编辑 tunasync worker 的配置文件添加一个脚本,使得每个同步作业结束后均清除前端上这个镜像的所有缓存:

#tunasync worker的配置文件
[global]
……
exec_on_success = [ "/home/mirror/postexec.sh" ]
exec_on_failure = [ "/home/mirror/postexec.sh" ]
……

#上述的postexec.sh脚本内容
#!/bin/bash
MIRROR_DIR="/mirror/"
DIR="${TUNASYNC_WORKING_DIR/"$MIRROR_DIR"/}"
/usr/bin/curl --silent --output /dev/null --request PURGE "https://mirror.nju.edu.cn/$DIR/*"

镜像站某些状态文件会频繁的周期更新,每次更新后都用 curl --silent --output /dev/null --request PURGE "https://mirror.nju.edu.cn/status/*" 清除一下缓存。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据