1概述
LNMP架构下 发生502 bad gateway 一般有两种情况
1,没有可用的php-fpm 没有可用的worker进程
2,脚本执行时间超过了 php-fpm request_terminate_timeout 设置的值 这时 worker进程会被kill 或者 request_terminate_timeout 设置的值过大 导致整个worker进程长时间工作不能释放 没有空闲的进程来处理请求从而报502
我们知道控制php脚本执行时间 有两个值 一个是php.ini 的 max_execution_time 和php-fpm 的request_terminate_timeout
我们可以分析下 超过最大请求时间报502错误的原因
2环境
为了测试的方便 可以将nginx 设置一个进程 php-fpm 设置 pm = static 模式 pm.max_children=1 request_terminate_timeout = 3
请求本地localhost/index.php 文件类容为sleep(20);
浏览器执行ocalhost/index.php
3分析
3.1先查看nginx 和 fpm错误日志
fpm错误日志
1,脚本执行超时
2,子进程收到SIGTERM信号退出了
3,开启了一个新的进程
可以看出 脚本执行超时 worker进程就会退出 ngixn 的报错 connection reset by peer 是因为这个worker进程退出导致的 tcp连接中一方断掉的话 会给对方发RST
3.2使用strace -p 来追踪进程
strace -p 11032 追踪 ngixn worker进程 如下图
3.2.1,epoll_wait(9...) = 1 表示有一个文件描述符有事件发生 此处为可读
3.2.2,accept4(6,....)=3 表示 accept接受请求 浏览器端口37226 IP 127.0.0.1 该socket fd=3
3.2.3,epoll_ctl(9,EPOLL_CTL_ADD,3,....) 将文件描述符3 加入到事件监听机制里面
3.2.4,recvfrom(3,.......) 表示 读取 从发生可读事件的socket描述符3 中读取内容 http协议
3.2.5,stat() 判断客户端请求的文件是否存在 存在返回0
至此 nginx 和客户端交互到一段落 接下来就是nginx和fpm的通信
3.2.6,socket(_)创建一个socket fd=11
3.2.7,connect(11,,9000,127.0.0.1,,) 请求的是127.0.0.1 port为9000 nginx 和php-fpm是用tcp socket 方式
3.2.8,writev(11,........808)=808 向fd11中发送数据 大小为808字节
3.2.9,recvfrom(11,.....) = -1 ECONNRESET (Connection reset by peer) 从 fd11 读到ECONNRESET (Connection reset by peer)
3.2.10,writev(5,'''''')=249 向fd5中写入错误信息,fd5就是nginx错误日志的文件描述符。
3.2.11,close(11) 关闭和fd11的连接
3.2.12,writev(3,......) 向fd3 (浏览器) 发送502 bad Gateway
3.2.13,向fd4写入一条访问日志,fd4就是nginx访问日志的文件描述符。
以上就是浏览器输入一个url nginx 发生的事 , 接下来追踪下php-fpm 的 worker进程
3.3追踪php-fpm
在上面介绍的 3.2.6 3.2.7 步骤中 nginx 向fpm发起tcp连接
3.3.1 accept(10 ...46554 ,,1270.0.01) = 5 accpet nginx 的请求 (socket connetc bind listen 在phpfpm master进程中执行) 文件描述符为5 端口号46554 ip 127.0.0.1
3.3.2 5次从发生可读事件的socket 描述符为5中读取数据
3.3.3 到最后 nanosleep(20,0) 脚本中slee(20) 然后执行 kill(12194)
4总结
使用tcpdump 抓包分析了nginx和php-fpm tcp情况可以发现 在进过三次握手之后
nginx 向fpm发送数据 fpm返回ACK 3s之后 fpm 回复了RST (tcp连接中 一方断掉 会给一方发送RST)
nginx 和fpm 通信方式有两种 一种是 tcp socket 另一种 unix socket
tcp socket 当nginx 和php-fpm不在同一个服务器时 只能使用这种方式
unix socket 当nginx和php-fpm在同一台服务器上时可以使用该方式 进程间通信 相对于tcp更加高效
因为 unix socket 不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等 是需要将应用数据从一个进程拷贝到另一个进程中
盗用一张图来表示几种方式