前言

之前尝试过高并发压力测试自己的网站,发现打到128并发基本上就趴窝了。
后面与友链的一个站长交流,得知可以通过缓存的方式解决这种高并发处理问题。
因为如果不设置缓存的话,即使是同样的请求,也会让php进行反复处理,消耗大量资源。
所以,我想尝试一下能不能改善一下这个情况。

实验环境准备

为了保证我网站能在我进行实验期间正常运行,我决定对我网站数据进行克隆,迁移到我云服务器里面性能最孱弱的那个进行测试。
那就是,白嫖的华为云:

  • 母鸡CPU: Intel(R) Xeon(R) Gold 6348 (2) @ 2.60 GHz
  • 双核CPU
  • 512MB内存
  • 20G硬盘

然后是软件环境,操作系统为Alpine Linux 3.20
然后我按照前面我那片迁移博客的方法,把博客迁移了过去。

原始表现

迁移过程就进行省略了,和前面的博客方法大体相同。
我测试下来,32并发就是这玩意的极限了,CPU彻底吃满。
再往上加线程已经没有意义了,因为已经寄了。
可以看出来,云服务器相比于实体鸡性能差别还是很大的。

优化

改用Unix Socket进行PHP对接

首先是php对接的问题,默认Alpine使用的是TCP网络协议进行本地回环,开销较大。而使用Unix Socket则可以解决这个问题,他具有低延迟资源占用少的优点。

特征TCP 端口Unix Socket
通信范围支持远程通信仅限于同一主机内的进程间通信
性能较高开销,适合长距离通信更高效,尤其在本地通信中表现出色
安全性需要额外的安全措施,如防火墙规则依赖于操作系统权限控制,天然更安全
配置复杂度需要设置IP地址和端口号只需指定文件路径

为了实现这个目标,我们需要对PHP的配置文件和Nginx的配置文件进行更改。

1. 修改 PHP-FPM 配置文件

首先,我们需要编辑 PHP-FPM 的池配置文件 /etc/php82/php-fpm.d/www.conf,将其 listen 设置更改为一个 Unix socket,并设置适当的权限。

[www]
; 更改 listen 为 Unix socket
listen = /var/run/php-fpm82/php8.2-fpm.sock

; 设置 socket 文件的所有者和组
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

确保 /var/run/php-fpm82/ 目录存在并且有正确的权限:

sudo mkdir -p /var/run/php-fpm82/
sudo chown root:nginx /var/run/php-fpm82/
sudo chmod 755 /var/run/php-fpm82/

2. 更新 Nginx 配置文件

然后,编辑你的 Nginx 站点配置文件,例如 /etc/nginx/http.d/nekopara.conf,将 fastcgi_pass 指令更新为指向新的 Unix socket:

location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass unix:/var/run/php-fpm82/php8.2-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

3. 重启服务

保存所有更改后,重启 PHP-FPM 和 Nginx 服务以使新配置生效。

# 重启 PHP-FPM
rc-service php-fpm82 restart

# 重新加载 Nginx
rc-service nginx reload

修改完成后,再进行测试,似乎没有太大改善,还是CPU爆炸。
x_20241214_205148.png
那就尝试一下设置缓存了。

为Nginx配置缓存规则

1. 定义 FastCGI 缓存区

首先,在 Nginx 的主配置文件 /etc/nginx/nginx.conf 添加一个 fastcgi_cache_path 指令来定义缓存存储的位置和参数。

http {
    # 其他配置...

    # 定义 FastCGI 缓存路径
    fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=my_fastcgi_cache:16m inactive=60m;
    
    # 定义缓存键,通常包含请求方法、主机名和 URI
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
}

在主配置文件加上以上这段即可。
确保 /var/cache/nginx/ 目录存在并且有适当的权限:

sudo mkdir -p /var/cache/nginx/fastcgi
sudo chown nginx:nginx /var/cache/nginx/fastcgi
sudo chmod 755 /var/cache/nginx/fastcgi

2. 修改站点配置以启用 FastCGI 缓存

接下来,在你的站点配置文件,例如 /etc/nginx/http.d/nekopara.conf 中修改处理 PHP 请求的 location ~ \.php$ 块,加入 FastCGI 缓存相关指令。
以下是我的示例配置文件:

server {
    listen 4443 ssl proxy_protocol;
    server_name www.nekopara.uk;

    #确保后端正确跳转而不带上端口号
    set_real_ip_from 0.0.0.0;
    real_ip_header proxy_protocol;

    set $real_host $http_x_forwarded_host;
    if ($http_x_forwarded_host = '') {
        set $real_host $host;
    }

    # 添加这一行,确保生成的重定向不带后端端口
    port_in_redirect off;

    # 设置最大上传文件大小为 8M
    client_max_body_size 8M;

    # SSL证书和密钥路径
    ssl_certificate /data/certs/nekopara_uk.pem;
    ssl_certificate_key /data/certs/nekopara_uk.key;

    # SSL设置(可选但推荐用于安全)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # 网站根目录
    root /data/typecho;
    index index.html index.php;

    # Serve static files directly
    location / {
        try_files $uri $uri/ /index.php?$args;
        autoindex off;
    }

    # PHP处理,并启用FastCGI缓存
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/var/run/php-fpm82/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # 启用 FastCGI 缓存
        fastcgi_cache my_fastcgi_cache;
        fastcgi_cache_valid 200 301 302 404 1m; # 设置缓存的有效时间
        fastcgi_cache_methods GET HEAD; # 只缓存 GET 和 HEAD 请求

        # 不缓存 POST 请求和其他非幂等性请求
        set $skip_cache 0;
        if ($request_method = POST) {
            set $skip_cache 1;
        }
        if ($query_string != "") {
            set $skip_cache 1;
        }
        if ($http_cookie ~* "PHPSESSID|_sf|your-session-cookie") { # 如果存在特定会话 cookie,则不缓存
            set $skip_cache 1;
        }

        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;

        # 添加头部信息显示缓存状态
        #add_header X-FastCGI-Cache $upstream_cache_status;
    }

    # Deny access to hidden files
    location ~ /\.ht {
        deny all;
    }
}

3. 测试并应用配置

保存所有更改后,测试 Nginx 配置文件是否正确:

nginx -t

如果一切正常,重新加载 Nginx 使新配置生效:

rc-service nginx reload

此时在进行并发压力测试,感觉就是在挠痒痒:
x_20241214_212040.png
即使上压力到256线程,也就那样:
x_20241214_212124.png

Nginx FastCGI 缓存配置参数解释

1. fastcgi_cache_path

fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=my_fastcgi_cache:16m inactive=60m;
  • /var/cache/nginx/fastcgi:指定缓存文件存储的路径。
  • levels=1:2:定义缓存目录结构为两级子目录,有助于分散文件,避免单个目录下文件过多导致性能问题。
  • keys_zone=my_fastcgi_cache:16m:创建一个名为 my_fastcgi_cache 的共享内存区域,大小为 16MB。这个区域用于存储缓存键(key)和元数据(如过期时间)。16MB 可以存储大约 80,000 个缓存项。
  • inactive=60m:设置缓存条目在多长时间内没有被访问就会被认为是不活跃的并从缓存中移除。这里设置为 60 分钟。

2. fastcgi_cache_key

fastcgi_cache_key "$scheme$request_method$host$request_uri";
  • 这个指令定义了用来生成缓存键的变量组合。每个唯一的 URL 请求(包括协议、请求方法、主机名和 URI)都会生成一个唯一的缓存键,确保不同的请求不会互相干扰。

3. fastcgi_cache_valid

fastcgi_cache_valid 200 301 302 404 1m;
  • 指定哪些 HTTP 状态码应该被缓存以及它们的有效期。例如,这里表示状态码 200 (OK)、301 (永久重定向) 和 302 (临时重定向) 404 (未找到页面) 的响应会被缓存 1 分钟。

4. fastcgi_cache_methods

fastcgi_cache_methods GET HEAD;
  • 指定只缓存特定的 HTTP 请求方法,这里是 GET 和 HEAD 请求。POST 请求通常包含表单提交等操作,不应该被缓存。

5. $skip_cache 和相关条件

set $skip_cache 0;
if ($request_method = POST) {
    set $skip_cache 1;
}
if ($query_string != "") {
    set $skip_cache 1;
}
if ($http_cookie ~* "PHPSESSID|_sf|your-session-cookie") {
    set $skip_cache 1;
}

fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
  • $skip_cache:这是一个自定义变量,用于标记是否跳过缓存。初始值设为 0,意味着默认情况下启用缓存。
  • 条件判断:通过一系列 if 语句检查请求是否是 POST 请求、是否有查询字符串或者是否存在特定的会话 cookie。如果任意条件满足,则将 $skip_cache 设置为 1,即禁用缓存。
  • fastcgi_cache_bypassfastcgi_no_cache:这两个指令分别控制是否绕过缓存(即直接发送请求给后端)和是否不让缓存当前请求的结果。当 $skip_cache1 时,两者都生效,确保动态或个性化内容不被缓存。

6. add_header X-FastCGI-Cache

add_header X-FastCGI-Cache $upstream_cache_status;
  • 向 HTTP 响应头添加 X-FastCGI-Cache 字段,显示缓存的状态(如 HIT, MISS 等),便于调试和监控。这部分是可以不要的。

理解了这些缓存参数的意义,你就可以根据需要自己调节了。

博客状态

我发布这篇文章的时候,已经完成了这些优化并且把博客迁移上云了。
毕竟白送的服务器,不用白不用。虽然云服务器隐私安全一直是一个问题,云服务器提供商能不能靠得住这点存疑。
但是,如果只是博客这种,反正都是要公开的内容,上云又何尝不可?
当然,家里云还是会保留的,毕竟它的主要任务是NAS,让我随时随地可以访问我的文件,博客之前只是想在上面顺带弄着玩的玩具。
随着博客访问量逐渐大了起来,迁移上云给家里云减负何尝不是一个更好的选择?
这个云服务器已经白嫖续费到2029年了,所以这个博客至少也能在上面运行差不多五年时间,已经比我购买的域名时间还长了。
迁移吧!
定期把内容备份到家里云就是了。
PS:不知道你有没有注意到网站底部的服务器运行时间,从200多天变成了80多天?