除了某些土豪站用全闪以外,大多数开源镜像站都是以机械盘为主,当带宽充裕的时候IO显然会成为瓶颈,那么采用少量的SSD来提升性能几乎是唯一的办法。加SSD的方法有很多种:
- lvmcache bcache bcachefs:因在用ZFS所以没有测试这类方案,看上去比较复杂;
- ZFS L2ARC:实际测试基本无效果,zpool iostat 看L2ARC读取很少;
- 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/*"
清除一下缓存。