开源镜像站缓存加速

除了某些土豪站用全闪以外,大多数开源镜像站都是以机械盘为主,当带宽充裕的时候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缓存配置如下:

  • mirror.conf
http {
    ……
    map $http_origin $good_origin {
        ~*([.|/]|^)mirrorz.org$ 1;
        ~*(/|^)mirrors.cernet.edu.cn$ 1;
        default 0;
    }
    upstream zfs_server {
        server 192.168.10.10:80;
        keepalive 300;
    }
    proxy_cache_path /cache levels=1:2 use_temp_path=off keys_zone=mirror:128m inactive=28d max_size=8192g;
    ……
}
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;
    ……
    location / {
        include conf.d/proxy_zfs;
    }
    location /.mirrorz/ {
        if ($good_origin) {
            add_header 'Access-Control-Allow-Origin' "$http_origin" always;
            add_header 'Vary' "Origin";
            add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-TUNA-MIRROR-ID';
            add_header X-Cache-Status $upstream_cache_status;
        }
        include conf.d/proxy_zfs;
    }
    ……
}
  • proxy_zfs
proxy_pass http://zfs_server;
proxy_redirect ~*^http://[^/]+(:\d+)?(/.*)$ $2;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_cache mirror;
proxy_cache_key $uri;
proxy_cache_valid 200 7d;
proxy_cache_valid 301 302 1d;
proxy_cache_valid any 1m;
proxy_cache_lock on;
proxy_cache_lock_timeout 600s;
proxy_cache_lock_age 600s;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
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;
  • proxy_cache_path:
    use_temp_path=off 临时文件直接放到 cache 目录中,防止文件在 temp_path 和 cache_path 之间移动。缓存文件会先放在 temp_path 中,下载完了再移动到 cache_path 下,设定成 off 比较简单安全。
    keys_zone=mirror:128m 内存缓存区名称是 mirror 容量是128MB,每MB内存可以缓存八千个key,可以估算一下key的数量设定缓存区大小。
    inactive=28d 超过28天没有访问的缓存会被删除,无论是否 valid,防止缓存被长期未用的数据占用。
    max_size=8192g 要比缓存目录 /cache 容量小,如果缓存用尽 nginx 会报错 [crit] …… failed (2: No such file or directory) 而且会直接断开客户端链接。
  • proxy_cache mirror:匹配前面 proxy_cache_path 的内存缓存区名称。
  • proxy_cache_key $uri:请求的URL,用这个URL用作缓存的key,去掉了参数后的真实文件路径。只要这个key样则nginx用缓存中一样的数据回复。
  • proxy_cache_valid 200 7d; proxy_cache_valid 301 302 1d; proxy_cache_valid any 1m;:HTTP状态码200的缓存7天、301/302的缓存24小时、其它的缓存1分钟,缓存达到这个时间就会被抛弃,防止数据被长期缓存不能得到更新。
  • proxy_cache_lock on:当有多个请求同一个文件时(同一个key),只有第一个请求从反代中获取,通过加锁让后续的请求等待第一个请求完成后从缓存中获取,防止并发从后端中请求同一个文件,加重后端负载。
  • proxy_cache_lock_age 600s:第一个请求锁定600秒,到期后就释放锁,下一个请求发送到后端。
  • proxy_cache_lock_timeout 600s:后续的请求如果等待600秒还未从缓存中获取,就直接从后端获取,但是不缓存。
  • proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504:当请求后端时出现错误(error)或超时(timeout)时使用过期缓存,出现一些5xx错误也用过期缓存,过期的缓存总比没有好。
  • 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可能需要设定某些文件的不缓存:

server {
    listen 80;
    root /mirror;
    ……
    location ~ ^/no_cache/ {
        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 来减少垃圾评论。了解你的评论数据如何被处理