今天测试做压力测试QPS超过1万mysql redis直接报502 time_wait,查了下大多数是没有即时回收,采用tcp复用容易后程序出现了故障,记录一下解决方案。
php mysql连接数过多解决办法(使用YII框架)
查看连接数
netstat -n | wc -l # 总连接数
netstat -n | grep -i time_wait | wc -l # time_wait 连接数
netstat -anp # 查看占用端口过多的程序
tcp复用解决方案
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_timestamps=1
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_fin_timeout=30
net.ipv4.tcp_keepalive_time = 600
nginx fastcgi(php) 解决方案
upstream fastcgi_backend {
server 127.0.0.1:9000;
keepalive 60;
}
large_client_header_buffers 4 16k;
client_max_body_size 32m;
client_body_buffer_size 512k;
fastcgi_connect_timeout 300;
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 32k;
fastcgi_busy_buffers_size 64k;
fastcgi_temp_file_write_size 64k;
location ~ \.php$ {
fastcgi_pass fastcgi_backend;
fastcgi_keep_conn on;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
主要是 fastcgi_keep_conn on 与 upstream 的 keepalive
nginx + proxy (go) 解决方案
upstream gopic {
keepalive 60;
server 127.0.0.1:xxx;
}
server {
keepalive_requests 10000; # 默认100
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
mysql数据库连接解决方案
'db' => [
'class' => 'yii\db\Connection',
'dsn' => '*', 'username' => '*',
'password' => '*',
'charset' => 'utf8mb4',
'attributes' => [
PDO::ATTR_PERSISTENT => true
]
],
php redis连接数过多解决办法(Yii,predis,phpredis)
Yii redis 持久配置
'redis' => [
'class' => 'yii\redis\Connection',
'hostname' => '127.0.0.1',
'retries' => 3,
'port' => 6379,
'password' => '',
'socketClientFlags' => STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT,
],
predis redis 持久配置
$client = new \Predis\Client([
'scheme' => 'tcp',
'host' => $redis->hostname,
'port' => $redis->port,
'password' => $redis->password,
'persistent'=>true,
]);
phpredis redis 持久配置
$redis->pconnect('127.0.0.1', 6379);
php-fpm优化
参数调优
进程数
首先,我们关注一个前提设置: pm = static/dynamic,标识fpm子进程的产生模式
static(静态) :表示在fpm运行时直接fork出pm.max_chindren个worker进程
dynamic(动态):表示运行时fork出start_servers个进程,随着负载的情况,动态的调整,最多不超过max_children个进程。
一般推荐用static,优点是不用动态的判断负载情况,提升性能,缺点是多占用些系统内存资源。
static:worker进程 pm.max_children = 300 这个值原则上是越大越好
dynamic:worker进程 pm.start_servers = 20
dynamic:空闲状态 pm.min_spare_servers = 5 最小php-fpm进程数量
dynamic:空闲状态 pm.max_spare_servers = 35 最大php-fpm进程数量
max_children
这个值原则上是越大越好,php-cgi的进程多了就会处理的很快,排队的请求就会很少。
设置”max_children”也需要根据服务器的性能进行设定
一般来说一台服务器正常情况下每一个php-cgi所耗费的内存在20M左右
假设“max_children”设置成100个,20M*100=2000M
也就是说在峰值的时候所有PHP-CGI所耗内存在2000M以内。
假设“max_children”设置的较小,比如5-10个,那么php-cgi就会“很累”,处理速度也很慢,等待的时间也较长。
如果长时间没有得到处理的请求就会出现504 Gateway Time-out这个错误,而正在处理的很累的那几个php-cgi如果遇到了问题就会出现502 Bad gateway这个错误。
start_servers
pm.start_servers的默认值为2。并且php-fpm中给的计算方式也为:
{(cpu空闲时等待连接的php的最小子进程数) + (cpu空闲时等待连接的php的最大子进程数 - cpu空闲时等待连接的php的最小子进程数)/ 2};
用配置表示就是:min_spare_servers + (max_spare_servers - min_spare_servers) / 2;
一般而言,设置成10-20之间的数据足够满足需求了。
最大请求数max_requests
最大处理请求数是指一个php-fpm的worker进程在处理多少个请求后就终止掉,master进程会重新respawn一个新的。
这个配置的主要目的是避免php解释器或程序引用的第三方库造成的内存泄露。
pm.max_requests = 10240
当一个 PHP-CGI 进程处理的请求数累积到 max_requests 个后,自动重启该进程。
502,是后端 PHP-FPM 不可用造成的,间歇性的502一般认为是由于 PHP-FPM 进程重启造成的.
但是为什么要重启进程呢?
如果不定期重启 PHP-CGI 进程,势必造成内存使用量不断增长(比如第三方库有问题等)。因此 PHP-FPM 作为 PHP-CGI 的管理器,提供了这么一项监控功能,对请求达到指定次数的 PHP-CGI 进程进行重启,保证内存使用量不增长。
正是因为这个机制,在高并发中,经常导致 502 错误
目前我们解决方案是把这个值尽量设置大些,减少 PHP-CGI 重新 SPAWN 的次数,同时也能提高总体性能。
最长执行时间request_terminate_timeout
设置单个请求的超时中止时间. 该选项可能会对php.ini设置中的’max_execution_time’因为某些特殊原因没有中止运行的脚本有用. 设置为 ‘0’ 表示 ‘Off’.当经常出现502错误时可以尝试更改此选项。
这两项都是用来配置一个PHP脚本的最大执行时间的。当超过这个时间时,PHP-FPM不只会终止脚本的执行,还会终止执行脚本的Worker进程。
Nginx会发现与自己通信的连接断掉了,就会返回给客户端502错误。