线上是 PHP 8.1 + PHP-FPM,前面挂 nginx,机器 8C16G,接口 QPS 峰值大概 300,单次响应 P99 在 800ms 左右。有天开始零星出现 502,频率不高,一天十几次,nginx error log 里是 upstream timed out。先去看 FPM 的 slowlog,request_slowlog_timeout 设的 5s,结果抓到的堆栈全是 [pool www] pid 12345 后面跟一个空行,或者只到 ? 就断了,根本看不到卡在哪个函数。
我的判断是这些请求根本没跑到用户代码里,卡在 FPM 收包或框架启动前的阶段,slowlog 的采样点落不到有用的位置。于是换了两个动作。第一,把 request_terminate_timeout 从 0 改成 30s,让 FPM 自己杀掉卡死的 worker,而不是等 nginx 的 fastcgi_read_timeout 先超时——前者杀掉后 worker 会被回收,后者只是断开连接,worker 还占着。
; php-fpm.d/www.conf
request_terminate_timeout = 30s
request_slowlog_timeout = 3s
slowlog = /var/log/php-fpm/$pool.slow.log
第二,在 nginx 侧把超时和日志对齐,确认到底是谁先断:
fastcgi_read_timeout 35s;
fastcgi_connect_timeout 3s;
log_format fpm '$remote_addr $request $upstream_response_time $upstream_status';
改完跑了一周,502 变成 FPM 的 child exited on signal 15 记录,能对上具体时间点。再回头看那些请求,基本都是某个导出接口在拉大结果集,PDO 一次性 fetchAll 几十万行,内存顶到 memory_limit 被 kill,表现出来却像超时。
为什么这样做:slowlog 只能采到 PHP 执行栈,拿不到“等待”本身;terminate 是兜底,代价是会把正常但慢的长任务也杀掉,所以 30s 这个值必须按你业务里最慢的合法请求来定,不能拍脑袋。另外 signal 15 的日志会误导人以为是部署重启,得结合 pid 和时间对。
会失效的地方:如果你用了 pcntl 或队列 worker 常驻,request_terminate_timeout 会误杀;OPcache 预热脚本、大文件上传也不适合这个配法。我踩过的坑是把 terminate 设成 10s,结果夜里一个报表任务被反复中断,第二天才发现是它。
你们线上遇到 upstream timed out 时,是先调 nginx 的超时,还是先让 FPM 自己杀 worker?有没有人试过用 strace 挂到卡住的 pid 上看它到底停在 read 还是 write?


0 条评论
还没有评论,写下第一条。