Nginx+php+mysql超时问题总结

背景:2020/07/06 02:58:19 [error] 31066#0: *3921 upstream timed out (110: Connection timed out) while reading response header from upstream, client: XXXX, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "XXXXX"

Nginx报上述错误,被折磨得半死,网上一堆的博客,你抄我,我抄你,大部分都是知其然而不知其所以然,总结自己找到的有效方法原理,也欢迎大家批评指正;

个人技术架构:nginx+php/反向代理+mysql

目录

1 有关于超时的原因

2 Nginx调优

2.1 面向客户端的请求超时

2.2 面向连接方式的超时配置

2.3.1 php-fpm的方式

2.3.2 反向代理方式

3 php-fpm调优

3.1 用户组问题

3.2 php-fpm.conf调优

4 MySQL优化

5 某些技巧


1 有关于超时的原因

抛开所有软件所在操作系统性能参数不谈,有关于超时能出现的地方总结来说可以分为5类:

  1. nginx面向客户端;
  2. nginx面向后端,如面向php,负载均衡的二级服务器设备等;
  3. php处理;
  4. php面向MySQL
  5. MySQL处理;

通过上面5类我们可以明显发现,当超时问题出现的时候,我们要排查的是整个系统哪个过程出了问题,而不是单纯地只改某一个地方!!!所以造成了网上很多资料按照操作来没有效果。

2 Nginx调优

Nginx调优通过上面的逻辑,我们只要做好(1)(2)就可以了.

2.1 面向客户端的请求超时

这个是在nginx.conf的http模块配置,配置如下:

http {

    …

    keepalive_timeout  65;  #保持

    tcp_nodelay on;

    client_header_timeout 15;

    client_body_timeout 15;

    send_timeout 25;

    …

}

 

client_header_timeout

默认值 60s,上下文 http server(指可以放在http块和server块)

指定等待client发送一个请求头的超时时间(例如:GET / HTTP/1.1).仅当在一次read中,没有收到请求头,才会算成超时。如果在超时时间内,client没发送任何东西,nginx返回HTTP状态码408(“Request timed out”)

 

client_body_timeout

默认值 60,上下文 http server location

该指令设置请求体(request body)的读超时时间。仅当在一次readstep中,没有得到请求体,就会设为超时。超时后,nginx返回HTTP状态码408(“Request timed out”)

 

keepalive_timeout (长连接类型)

KeepAlive 模式,它告诉 webserver 在处理完一个请求后保持这个 TCP 连接的打开状态。若接收到来自客户端的其它请求,服务端会利用这个未被关闭的连接,而不需要再建立一个连接。

KeepAlive 在一段时间内保持打开状态,它们会在这段时间内占用资源。占用过多就会影响性能。

Nginx 使用 keepalive_timeout 来指定 KeepAlive 的超时时间(timeout)。指定每个 TCP 连接最多可以保持多长时间。Nginx 的默认值是 75 秒,有些浏览器最多只保持 60 秒,所以可以设定为 60 秒。若将它设置为 0,就禁止了 keepalive 连接。

 

lingering_timeout

默认值 5s

上下文 http server location

说明 lingering_close生效后,在关闭连接前,会检测是否有用户发送的数据到达服务器,如果超过lingering_timeout时间后还没有数据可读,就直接关闭连接;否则,必须在读取完连接缓冲区上的数据并丢弃掉后才会关闭连接。

 

resolver_timeout

默认值 30s

上下文 http server location

说明 该指令设置DNS解析超时时间

 

2.2 面向连接方式的超时配置

这里就要解释nginx与php的连接方式了,一种是访问本机的php-fpm,一种是反向代理的方式。主要配置在server配置节之内。

2.3.1 php-fpm的方式

原理:Nginx -> FastCGI -> php-fpm -> FastCGI Wrapper -> php解析器

 

CGI是通用网关协议,FastCGI则是一种常驻进程的CGI模式程序。我们所熟知的PHP-FPM的全称是PHP FastCGI Process Manager,即PHP-FPM会通过用户配置来管理一批FastCGI进程,例如在PHP-FPM管理下的某个FastCGI进程挂了,PHP-FPM会根据用户配置来看是否要重启补全,PHP-FPM更像是管理器,而真正衔接NginxPHP的则是FastCGI进程

图中,FastCGI的下游CGI-APP就是PHP程序。而FastCGI的上游是Nginx,他们之间有一个通信载体,即图中的socket。在我们上文图3的配置文件中,fastcgi_pass所配置的内容,便是告诉Nginx你接收到用户请求以后,你该往哪里转发,在我们图3中是转发到本机的一个socket文件,这里fastcgi_pass也常配置为一个http接口地址(这个可以在php-fpm.conf中配置)。而上图5中的Pre-fork,则对应着我们PHP-FPM的启动,也就是在我们启动PHP-FPM时便会根据用户配置启动诸多FastCGI触发器(FastCGI Wrapper)

PHP提供SAPI面向Webserver来提供扩展编程。但是这样的方式意味着你要是自主研发一套Webserver,你就需要学习SAPI,并且在你的Webserver程序中实现它。这意味着你的Webserver与PHP产生了耦合。解决耦合的办法:CGI协议,比较好的方式是有一套通用的规范,上下游都兼容它。那么CGI协议便成了Nginx、PHP都愿意接受的一种方式,而FastCGI常住进程的模式又让上下游程序有了高并发的可能。

连接方式unix sockettcp socket

unix socket

需要在nginx配置文件中填写php-fpm运行的pid文件地址。

location ~ \.php$ {

    fastcgi_pass unix:/var/run/php-fpm.sock;#根据你的路径来

}

tcp socket

需要在nginx配置文件中填写php-fpm运行的ip地址和端口号。

location ~ \.php$ {

    fastcgi_pass 127.0.0.1:9000;

}

弄懂了连接方式,那么server的配置方式如下:

server {

        listen       80;

        server_name  localhost;

        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;

        location ~ \.php$ {

                  fastcgi_pass 127.0.0.1:9000;

        }
}

从上面的配置中我们可以发现,缓存大小的设置对性能也是有影响的!

2.3.2 反向代理方式

这种就是你的请求转发给后端服务器的方式,反向代理不做过多介绍,具体例子如下:

server {

    listen       80;

    server_name  localhost;

    large_client_header_buffers 4 16k;     # 读取大型客户端请求头的缓冲区的最大数量和大小

    client_max_body_size 300m;     #设置nginx能处理的最大请求主体大小。

    client_body_buffer_size 128k;  #请求主体的缓冲区大小。

    proxy_connect_timeout 600;

    proxy_read_timeout 600;

    proxy_send_timeout 600;

    proxy_buffer_size 64k;

    proxy_buffers   4 32k;

    proxy_busy_buffers_size 64k;

    proxy_temp_file_write_size 64k;

    ……

}

提高nginx网络吞吐量buffers优化指令说明

large_client_header_buffers

此指令规定了用于读取大型客户端请求头的缓冲区的最大数量和大小。 这些缓冲区仅在缺省缓冲区不足时按需分配。 当处理请求或连接转换到保持活动状态时,释放缓冲区。

client_max_body_size

此指令设置NGINX能处理的最大请求主体大小。 如果请求大于指定的大小,则NGINX发回HTTP 413(Request Entity too large)错误。 如果服务器处理大文件上传,则该指令非常重要。

client_body_buffer_size

此指令设置用于请求主体的缓冲区大小。 如果主体超过缓冲区大小,则完整主体或其一部分将写入临时文件。 如果NGINX配置为使用文件而不是内存缓冲区,则该指令会被忽略。 默认情况下,该指令为32位系统设置一个8k缓冲区,为64位系统设置一个16k缓冲区。 该指令在NGINX配置的http,server和location区块使用。

 

nginx代理超时配置

proxy_connect_timeout

默认值60s, nginx连接到后端服务器的连接超时时间,发起握手等候响应超时时间。

proxy_read_timeout

连接成功后,等候后端服务器响应时间。其实已经进入后端的排队之中等候处理(也可以说是后端服务器处理请求的时间)。

proxy_send_timeout

后端服务器数据回传时间,就是在规定时间之内后端服务器必须传完所有的数据。

注:nginx使用proxy模块时,默认的读取超时时间是60s。

 

nginx缓存区大小设置

proxy_buffer_size

nginx可从服务器一次接收的最大数据大小

proxy_buffers

该指令设置缓冲区的大小和数量,从被代理的后端服务器取得的响应内容,会放置到这里. 默认情况下,一个缓冲区的大小等于内存页面大小,可能是4K也可能是8K,这取决于平台。

proxy_busy_buffers_size

proxy_busy_buffers_size不是独立的空间,他是proxy_buffers和proxy_buffer_size的一部分。nginx会在没有完全读完后端响应的时候就开始向客户端传送数据,所以它会划出一部分缓冲区来专门向客户端传送数据(这部分的大小是由proxy_busy_buffers_size来控制的,建议为proxy_buffers中单个缓冲区大小的2倍),然后它继续从后端取数据,缓冲区满了之后就写到磁盘的临时文件中。

proxy_max_temp_file_sizeproxy_temp_file_write_size

临时文件由proxy_max_temp_file_size和proxy_temp_file_write_size这两个指令决定。

proxy_temp_file_write_size 是一次访问能写入的临时文件的大小,默认是proxy_buffer_size和proxy_buffers中设置的缓冲区大小的2倍,Linux下一般是8k。

proxy_max_temp_file_size 指定当响应内容大于proxy_buffers指定的缓冲区时, 写入硬盘的临时文件的大小. 如果超过了这个值, Nginx将与Proxy服务器同步的传递内容, 而不再缓冲到硬盘. 设置为0时, 则直接关闭硬盘缓冲.

 

3 php-fpm调优

3.1 用户组问题

这个问题比较欺负菜鸟,当然,老玩家也会因为一时大意,未配置号权限造成访问超时的问题。在php-fpm.conf中配置:

listen.owner = nobody

listen.group = nobody

user = nobody

group = nobody

这里最好与nginx的用户一样,有的人会问,为啥要配置成nobody?我偏不行不行?行,你想叫啥就叫啥,为了安全最好不要配置成高权限的用户,如root。

3.2 php-fpm.conf调优

pm = static | dynamic | ondemand

pm.max_children = 15

pm.start_servers = 5

pm.min_spare_servers = 5 //空闲时间最小的php-fpm进程

pm.max_spare_servers = 5 //空闲时间最大的php-fpm进程

pm.max_requests = 1024

request_terminate_timeout = 0

 

pm = static | dynamic | ondemand

static模式下php-fpm子进程数量固定;dynamic就是动态分配

动态适合小内存机器,灵活分配进程,省内存。静态适用于大内存机器,动态创建回收进程对服务器资源也是一种消耗。

如果你的内存很大,有8-20G,按照一个php-fpm进程20M算,100个就2G内存了,那就可以开启static模式。如果你的内存很小,比如才256M,那就要小心设置了,因为你的机器里面的其他的进程也算需要占用内存的,所以设置成dynamic是最好的,比如:pm.max_chindren = 8, 占用内存160M左右,而且可以随时变化,对于一半访问量的网站足够了。

pm.max_children

最大可创建的子进程的数量。看你机子情况了,设置大了没意义。

一般来说一台服务器正常情况下每一个php-cgi所耗费的内存在20M~30M左右,因此看你机座的具体情况。

如果长时间没有得到处理的请求就会出现 504 Gateway Time-out 这个错误,而正在处理的很累的那几个php-cgi如果遇到了问题就会出现 502 Bad gateway 这个错误。

pm.start_servers

随着php-fpm一起启动时创建的子进程数目。默认值:min_spare_servers + (max_spare_servers - min_spare_servers) / 2。

pm.min_spare_servers

设置服务器空闲时最小php-fpm进程数量。必须设置。如果空闲的时候,会检查如果少于10个,就会启动几个来补上。

pm.max_spare_servers

设置服务器空闲时最大php-fpm进程数量。必须设置。如果空闲时,会检查进程数,多于30个了,就会关闭几个,达到30个的状态。

pm.max_requests

最大处理请求数是指一个php-fpm的worker进程在处理多少个请求后就终止掉,master进程会重新respawn新的。该配置可以避免php解释器自身或程序引起的memory leaks。上面的配置最大请求数:15*1024=15360,最小请求数:5*1024=7120。

request_terminate_timeout

表示将执行时间太长的进程直接终止。这个也要根据自己机子实际情况来,设置为0的时候表示请求时间将没有超时限制。为了防止php有循环bug,最好设置一个值。默认单位s,默认值0.一般设置10分钟左右应该差不多了。

4 MySQL优化

MySQL优化是个大课题,针对我目前得现状我只在数据库内部改了interactive_timeout与wait_timeout,其它的大家可以一起讨论一下。

interactive_timeout 参数含义:服务器关闭交互式连接前等待活动的秒数。交互式客户端定义为在mysql_real_connect()中使用CLIENT_INTERACTIVE选项的客户端。 参数默认值:28800秒(8小时);

wait_timeout: 参数含义:服务器关闭非交互连接之前等待活动的秒数。 在线程启动时,根据全局wait_timeout值或全局interactive_timeout值初始化会话wait_timeout值,取决于客户端类型(由mysql_real_connect()的连接选项CLIENT_INTERACTIVE定义)。 参数默认值:28800秒(8小时);

这两个参数决定一个持续SLEEP状态的线程多久被关闭。如果你的MySQL Server有大量的闲置连接,他们不仅会白白消耗内存,而且如果连接一直在累加而不断开,最终肯定会达到MySQL Server的连接上限数,这会报'too many connections'的错误。对于wait_timeout的值设定,应该根据系统的运行情况来判断。在系统运行一段时间后,可以通过show processlist命令查看当前系统的连接状态,如果发现有大量的sleep状态的连接进程,则说明该参数设置的过大,可以进行适当的调整小些。

设置和查看:

mysql> show global variables like 'wait_timeout'; 

mysql> show global variables like 'interactive_timeout';

mysql> set global interactive_timeout=1800;

mysql> set global wait_timeout=1800;

5 某些技巧

看这类超时问题我的方法是逐一用jmeter压测排查问题出现在哪里,如果php运行时间过长还可以开启慢日志来寻找错误。在php-fpm下设置:

slowlog = /usr/local/var/log/php-fpm.log.slow

request_slowlog_timeout = 15s

当一个请求该设置的超时时间15秒后,就会将对应的PHP调用堆栈信息完整写入到慢日志中。php-fpm慢日志会记录下进程号,脚本名称,具体哪个文件哪行代码的哪个函数执行时间过长。

总结:有关于nginx+php+mysql优化一直是一个大问题,本人总结了目前我所能用到的一些情况,欢迎大家批评指正。

参考资料:

Nginx与PHP交互过程 + Nginx与PHP通信的两种方式:https://blog.csdn.net/wuhuagu_wuhuaguo/article/details/83032578

nginx配置文件参数详解:https://www.cnblogs.com/zclzhao/p/5033391.html

nginx中的超时设置,请求超时、响应等待超时等:https://www.cnblogs.com/lemon-flm/p/8352194.html

MySQL中interactive_timeout和wait_timeout的区别:https://www.cnblogs.com/ivictor/p/5979731.html

interactive_timeout和wait_timeout:https://www.cnblogs.com/zengkefu/p/5651910.html

php-fpm安装、配置与优化:https://blog.csdn.net/ivan820819/article/details/54970330?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase

Nginx+FastCGI到底是谁影响超时时间:http://www.linuxidc.com/Linux/2014-10/108012.htm

nginx响应超时upstream timed out (110: Connection timed out) while reading response header from upstream:https://blog.csdn.net/u014218983/article/details/81217032#nginx%E7%BC%93%E5%AD%98%E5%8C%BA%E5%A4%A7%E5%B0%8F%E8%AE%BE%E7%BD%AE

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章