1. 网络实践

去网络上学习,大多数告诉你,统计每个php进程内存,然后系统内存/php进程最大内存,php max_children就设置为那个值。

但是这种做法不是很精准,需要根据你自己的业务在单台服务器上跑,进行压测,实践出最合适的值,否则有可能内存充足,但是cpu 100%。

//查看php进程内存占用情况

ps -ylC php-fpm --sort:rss //rss 字段表示内存占用 千字节 kb

//平均占用内存

ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }'

//官方建议设置值

pm.max_children = Total RAM dedicated to the web server / Max child process size

2. Nginx实践

2.1、nginx进程数设置

worker_processes auto;//自动根据cpu启动多少个nginx进程

2.2、使用epoll

events {
    use epoll;
    worker_connections 655350;
    multi_accept on;
}

2.3、开启压缩

gzip on;
gzip_min_length 1k;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml 
text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";

2.4、限流

在http段定义限流相关参数

//通过geo定义白名单,默认为1,白名单内为0
geo $limit {
    default 1;
    127.0.0.1 0;
    xx.xx.xx.111 0;
}
//0的不限流  1的会走限流
map $limit $limit_key {
    0 "";
    1 $binary_remote_addr;
}

//定义内存 10m存储ip地址,每个ip 1s可以发起4个请求,每250ms nginx放行一个请求,nginx的限流是毫秒级控制
limit_req_zone $limit_key zone=vlimit:10m rate=4r/s;

在server/location段进行实际的限流措施

//考虑突发性,burst允许为3,上面定义了每250ms一个请求
//这边burst=3 nodelay 表示第一个250ms最大允许1+3个请求,后面每250ms放行一个请求。

limit_req zone=vlimit burst=3 nodelay; 

3. PHP-FPM实践

3.1、基本参数

pm = static  //static,dynamic,ondemand

pm.max_children = 384  //最大进程数

pm.start_servers = 125 //主进程启动的时候,启动的进程

pm.min_spare_servers = 100  //空闲时最低的备用进程数

pm.max_spare_servers = 15 //空闲时最大的备用进程数

pm.max_requests = 1024  //每个进程接受了多少请求之后,进行销毁

3.2、pm

这个选择要看具体场景,选择static还是dynamic。如果服务器只有nginx+php可以考虑采用static减少开销。

3.3、max_children

建议是要看具体压测适合哪个值,也有说根据cpu核数的倍数进行设置,8倍,16倍,32倍

3.4、pm.max_requests

看压测去,避免设置的过低导致cpu频繁切换导致100%,如果太大容易504

3.5 使用tcp方式还是unix方式

修改php-fpm.d/www.conf,listen 采用127.0.0.1:9000还是/dev/shm/php-cgi.sock

nginx配置

#fastcgi_pass   127.0.0.1:9000;
fastcgi_pass   unix:/dev/shm/php-cgi.sock;

也看压测结果去。同一台服务器可以考虑unix方式,减少通信开销,但是部分应用也有可能在高并发的时候不稳定。

4. 系统参数

vi /etc/sysctl.conf

net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.ipv4.tcp_max_tw_buckets = 6000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096 87380 4194304
net.ipv4.tcp_wmem = 4096 16384 4194304
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.netdev_max_backlog = 262144
net.core.somaxconn = 65535
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 524288 699050 1048576
net.ipv4.tcp_fin_timeout = 1
net.ipv4.tcp_keepalive_time = 30
net.ipv4.ip_local_port_range = 1024 65000

注意tcp_max_syn_backlog,netdev_max_backlog,somaxconn参数。

执行 sysctl -p生效。

5. 问题总结

php项目多数场景是高IO的,程序花很多时间都是在与redis和mysql交互,cpu多数处于等待情况下。

我之前遇到web服务器100%的时候,内存占用只有30-40%,所以要根据具体情况进行压测,才好充分利用服务器的资源。

否则无法识别出cpu 100%是因为max_request设置不当,导致cpu 维护php进程导致使用率100%还是php-fpm本身启动进程太多导致的。