摘要
正在生成中……
现有一个系统是用Yii2
框架开发的,Web服务器采用Nginx
+php-fpm
,由于没有使用Nginx
的反向代理缓存技术,用Apache
的ab
一压就死掉了,QPS
只能达到7
或者8
的水平,像这样是无法支持高并发访问的:
ab -n 500 -c 100 https://front.we.com/site/robot
用htop
查看主要压力在cpu
上,而占用cpu
最多的是几个php-fpm
进程,说明nginx
只是简单地做了一个二传手,把所有压力全部转嫁到了php
身上,考虑到nginx
本身有反向代理缓存技术,因此直接配置好应该就可以了。网上教程一般都是要再安装一个Apache
服务器,由Nginx
转Apache
再转php
,考虑到我们已经有php-fpm
了,就不想再安装一个多余的web服务器。
首先,在nginx
的主配置文件里先设置好与缓存相关的配置:
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
proxy_cache_path /var/run/proxy_cache levels=1:2 keys_zone=czone:10m max_size=1000m inactive=600m use_temp_path=off;
proxy_cache_key $scheme$host$request_uri;
proxy_buffers 256 16k;
proxy_buffer_size 32k;
proxy_set_header Proxy "";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
这里需要先手工建立与缓存相应的目录/var/run/proxy_cache
,并且把它设置为可读写。keys_zone
的czone
这个名字可以改成任意名字,只要和下文相对应即可。
然后,在相应的服务器设置里作如下配置:
-
先增加一个虚拟的上游服务,这个front
这个名字由你自己决定:
upstream front {
ip_hash;
server unix:/var/run/nginx_front.sock;
}
-
将原先监听80
端口或者监听443
端口的设置改为监听上述虚拟端口:
server {
listen unix:/var/run/nginx_front.sock;
server_name front.we.com;
root /var/www/we/frontend/web;
location / {
index index.php index.html index.htm;
if (!-e $request_filename) {
rewrite ^/(.*) /index.php?r=$1 last;
}
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
add_header 'Referrer-Policy' 'no-referrer';
}
-
再增加一个server
监听80
或443
端口:
server {
listen 443 ssl http2;
server_name front.we.com;
root /var/www/we/frontend/web;
ssl on;
ssl_certificate /etc/letsencrypt/live/front.we.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/front.we.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
gzip on;
gzip_min_length 1k;
gzip_buffers 16 64k;
gzip_http_version 1.1;
gzip_comp_level 5;
gzip_proxied any;
gzip_types text/plain application/x-javascript application/javascript application/octet-stream text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png image/svg image/svg+xml;
gzip_vary on;
add_header X-Cache $upstream_cache_status;
location ~ /.well-known {
allow all;
}
location ~ /\. {
return 403;
}
location /favicon.ico {
root html;
}
location ~* \.(css|js|gif|ico|jpg|png|svg)$ {
expires 365d;
}
location / {
proxy_set_header Host $host;
proxy_redirect off;
proxy_cache czone;
proxy_cache_revalidate on;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_cache_valid 200 301 302 500m;
proxy_cache_valid 404 1m;
proxy_pass http://front;
}
}
注意这里的两个地方:一个是proxy_cache
的czone
和我们上面设置的keys_zone
要对应,另一个proxy_pass
和上面设置的虚拟上游服务器对应。中间加了一个X-Cache
的header
主要是为了方便我们在浏览器端查看缓存起没起作用而设的,因为如果缓存命不中的话,压力还是在php上。
设置完成之后,可以先用nginx -t
测试一下配置是否正确:
/usr/local/nginx/sbin/nginx -t
一切都正常的话,重新加载配置文件:
/usr/local/nginx/sbin/nginx -s reload
这时如果用浏览器直接访问Yii2
生成出来的网页,很有可能缓存还是命不中,如下图:
注意看这里的倒数第二行X-Cache: MISS
,说明缓存没有命中。为什么呢?注意上面第一行,Cache-Control
里面有一个no-cache
,Yii2
缺省的所有controller
都是无缓存的,所以导致了这里即使nginx
已经配置好缓存,也起不了作用。所以我们还需要改造Yii2
,在behaviors
里增加一个HttpCache
的设置,注意下面的lastModified
必须有,否则也不起作用,这里简单地取了user
表中最后一次更新的时间作为lastModified
,理论上应该设置更符合业务应用的方法。
public function behaviors()
{
return [
[
'class' => 'yii\filters\HttpCache',
'only' => ['home', 'index', 'robot', 'about'],
'lastModified' => function ($action, $params) {
$q = new \yii\db\Query();
return $q->from('user')->max('updated_at');
},
],
];
}
现在,再次刷新网页,你就可以看到X-Cache: HIT
了。
至此,由Yii2
生成的静态网页的压力不再传递到php
而转交给nginx
,我们再次执行ab
压一下看:
每秒116QPS
,比我们不用缓存时性能提升接近15
倍之多!而且用htop
查看,压测时CPU
负担都在nginx
上,不在php
上。我用的服务器比较渣,只有2核2g,如果服务器再好一些,肯定性能还会有极大提升。另外php
如果升级成7.0
,对不用缓存的部分性能也会有提升,这就不在本文讨论的范围之内了。