一、nginx 和 php-fpm 的关系和分工
nginx 是 web 服务器, php-fpm 是一个 PHPFastCGI 进程管理器,两者遵循 fastcgi 的协议进行通信, nginx 负责静态类似 html 文件的处理, php-fpm 负责 php 脚本语言的执行,这么设计的目的是为了解耦前端 nginx 和后端的 php ,不至于让容易出问题的 php 脚本堵塞整个 nginx 的业务处理,影响用户体验,因为 php 脚本语言的执行是会比较容易出问题的。 nginx 之所以能处理成千上万高并发业务,除其本身的异步非阻塞模式,在与和其他模块的耦合扩展方法也是分不开的,在 nginx 的设计里不能接受的就是阻塞,不过并非完全没有梗,比如说用到的最多的多进程单线程的模式,由于 nginx 日志没有单独的处理进程,如果收集日志时处理不当就会把 worker 进程堵死。
对应nginx+php-fpm的模型结构图如下:
(图 1)
1、nginx 的工作简介(对应图1看)
在接到 php的脚本 请求后, nginx 通过 fastcgi_pass 指令将请求传递给后端 php-fpm 的 worker 进程处理,在此过程中, nginx 做了各种超时机制、缓存机制、 buffer 机制和长连接机制等来保障与后端的 php-fpm 能够良性高效的合作。
在超时机制方面控制 nginx 对后端 php 的等待时间,通过各种 timeout 指令进行控制,例如:
fastcgi_connect_timeout 后端链接时间
fastcgi_send_timeout 数据发送时间,两次成功发送时间差,不是整个发送时间
fastcgi_read_timeout 数据接收时间,两次成功接收时间差,不是整个接收时间
当超时后会返回 504 超时的状态码,在 buffer 机制指令也有很多,例如:
fastcgi_buffer_size 存放 fastcgi 传过来的响应头,一般设置为分页大小
fastcgi_buffers 存放 fastcgi 传过来的相应内容,一般设置分页的倍数,格式例如 8 4k|8k
另外还有一些其它的缓存、长连接机制不做介绍,当设置不合理时也会出现 5XX 错误,nginx的文章介绍写了有很多的,不再做过多的说明。
2、php-fpm 工作介绍(对应图1看)
Php-fpm 是一个 PHPfastcgi 进程管理器,在启动后会有 master 和 worker 两种进程, master 负责接收外部信号和管理 worker 进程, worker 进程是负责干活的,处理 nginx 传过来的任务。
master 进程只有一个,负责监听端口和管理 worker 进程,每次传来任务,与前端的 nginx 建立 3 次握手后放入连接队列,供 worker 进程进行 accept ,当 worker 进程出现错误或执行超时时,负责将 worker 进程重启或者杀掉,是 php-fpm 模型中的大内总管。
Worker 进程是工作进程,每个 worker 进程都独立的执行 php 程序脚本,然后把执行的结果通过 fastcgi 协议交给 nginx ,执行过程中受 master 的管理。在工作中, worker 进程去竞争 accept 管理进程 master 的链接队列, accept 函数将从连接请求队列中获得连接信息,创建新的 socket ,并返回该套接字的 fd ,新创建的 socket 用于服务器与 nginx 的通信,而原来的套接字仍然处于监听状态。
php-fpm 可以配置多个 pool ,所有 pool 由 master 统一管理监听不同端口并分配不同 worker 进程池, worker 进程池支持动态 prefork 同时也支持静态开启,服务器内存较大时建议直接计算后配置静态资源池,可以减少频繁 prefork 进程所带来的开销,提高服务质量,由于进程模型越跑程序耗费越大,因为每个 worker 进程可以配置执行多少个请求后进行重启,对应的池子的指令和执行多少个请求的指令如下:
pm = static | dynamic | ondemand 静态池、服务优先、内存优先
pm.max_children = 256 开启的最大 php 进程数
pm.max_requests = 1024 在执行了 1024 个请求后重启 worker 进程
这也是我们线上服务器的配置,我们线上用的 web 服务的机器是 12 核 cpu 、 16G 内存, nginx 开启 12 个 worker 进程, php 开启 256 个进程,跑起来后每个进程大概占用 30M 内存,也就是( 256+12 ) *30=8G ,另外还跑了一些配管、监控、统计、日志收集等七七八八的软件,整体业务是比较轻松的,这种静态池的配置大大减少了 prefork 进程带来的开销,RT时间100ms以内的占到85%(这个与程序写的如何有关),运行一段时间后的开销截图如下:
二、此模型结构常见的5XX服务器端错误及优化(对应图1看)
1、nginx日志里 产生 502 错误
第一种情况, php-fpm的worker进程 执行 php 程序脚本时,超过了配置的最长执行时间, master 进程将 worker 进程杀掉,直接返回 502 。
返回 502 后 nginx 对应的 error 日志是 104: Connection reset by peer
对应的 php 执行时间的配置如下,一些版本中 php-fpm 的配置会覆盖 php.ini 的配置,使 php.ini 的配置不起作用:
php.ini 中默认 30s : max_execution_time =
php-fpm 中: request_terminate_timeout =
第二种情况, 连接请求数( accpet 之前)超出了端口所能监听的 tcp 连接的最大值( backlog 的值),进不了 fpm 等待 accept 的链接队列,直接返回 502 ,这里可能会产生 tcp 重传;
返回 502 后 nginx 对应的 error 日志是 111: Connection refused
backlog 的值是半连接和全连接的总和,他的存在也有短时间缓冲解耦 nginx 请求与 fpm 处理的作用,半连接指收到了 syn 请求, 3 次握手尚未建立,全连接指的是 3 次握手已经成功,不过尚未被 accpet 的请求, fpm 里面有调节的参数,如果 fpm 的参数设置为 -1 ,则默认走的是系统内核参数 net.core.somaxconn 的设置值,如果不设置可以在 /proc/sys/net/core/somaxconn 里查看,默认值是 128 ,所以在连接请求较高的业务里要增大这个值。
减少避免 502 报错优化建议
502主要从php-fpm的配置方考虑,根据服务器情况, 适量增大 php-fpm 的工作进程数,适当增加 php 的执行时间,适当增加 backlog 值 。
php 的工作进程数也不是越大越好,这种进程模型运行时间长了占的内存会增大,一般一个 php 进程是占到 30M 左右的内存,开多少合适自己算吧, nginx 的 worker 进程一般也能跑到 30M 的内存,综合计算一下; php 的执行时间可以根据你的服务标准来设定,超过服务时间浏览器返回的是 502 错误,这个按照实际的情况处理吧,反正我是觉得执行的慢有返回结果总比直接返回 502 错误的强;至于 backlog 值,当程序写的比较好时,建议设置其数量为 php 工作进程的 1 到 2 倍。
2、nginx日志里 产生 504 错误
第一种情况,php 的 worker 进程池处理慢,无法尽快处理等待 accept 的链接队列,导致 3 次握手后的链接队列长时间没有被 accept , nginx 链接等待超时;
返回 504 后 nginx 对应的 error 日志是 110: Connection timed out
第二种情况,后端php-fpm执行脚本的时间太长,超过了nginx配置的超时机制,这个时候也是会报出504错误的。
减少避免504报错的优化建议
504主要从nginx的配置方考虑,根据业务情况配置好超时的各种机制,包含但不限于下属参数:
fastcgi_connect_timeout
fastcgi_send_timeout
fastcgi_read_timeout
。。。。。。。。
另外: 在配置过程中,比如遇到大并发或者是特殊业务的场景,不合理的fd、buffer等设置也会带来5XX错误,比如说大并发连接的业务要增大系统和单个程序的fd数量,如果是上传业务要增大头buffer等,这些要视情况而做优化,正所谓道法自然,术变万千,要以不变应万变。
关注微信公众号:PHP技术大全
PHPer升级为大神并不难!