Nginx error_log 不直接记录“事件循环超时”,但通过 upstream timed out、connect() failed、worker shutting down 等日志可间接定位事件循环受阻根源,需结合 debug 日志、access_log 时长对比及系统指标交叉验证。

Nginx 的 error_log 本身不直接记录“事件循环超时”这一内部机制的细节,因为 Nginx 的事件循环(基于 epoll/kqueue)是底层调度逻辑,不会以“event loop timeout”形式打日志。但它是唯一能间接反映事件循环被阻塞或长时间挂起的关键证据源——关键在于识别那些本该快速完成、却因某种原因卡在 I/O 或上游等待中,最终触发超时并落进 error_log 的异常模式。
真正要分析的,不是“事件循环超时”,而是哪些外部行为正在拖住事件循环。error_log 提供了最贴近问题现场的线索。
error_log 中指向事件循环受阻的核心信号
-
upstream timed out (110: Connection timed out) while connecting to upstream
表示 Nginx worker 在发起 connect() 系统调用后,迟迟未收到 SYN-ACK。这通常不是事件循环本身卡死,而是:- 后端服务完全宕机或端口未监听(此时连接根本发不出去)
- 中间网络设备(如防火墙、安全组)静默丢弃 SYN 包 → worker 长时间阻塞在 connect() 上,占用一个 event loop slot
- 后端连接队列已满(listen backlog 溢出),新连接被内核延迟响应
-
upstream timed out (110: Connection timed out) while reading response header from upstream
连接已建立,但 worker 在recv()等待响应头时超时。这意味着:- 后端应用卡在数据库连接获取、慢 SQL、锁竞争、GC 停顿等环节,无法及时 write
- worker 被绑定在这个连接上,无法处理其他就绪事件 → 事件循环实质被单个请求拖慢
*`NNN connect() failed (111: Connection refused)
高频出现** 不是超时,但说明上游不可达。若伴随大量 502,worker 会频繁重试、快速失败,虽不卡住事件循环,但会抬高 CPU 并掩盖真实瓶颈。需与upstream timed out` 区分。worker process is shutting down+gracefully shutting down日志堆积
若 reload 后旧 worker 长时间不退出,说明它正卡在某个长连接(如 WebSocket、HTTP/2 流)或未完成的 upstream read 中 —— 这是事件循环未能及时释放资源的直接体现。
为什么不能只看 error_log?必须交叉验证
-
error_log只告诉你“哪里断了”,不告诉你“为什么断”。例如upstream timed out可能是 DB 慢、网络抖动、后端 OOM、甚至 Nginx 自身配置错误(如 proxy_read_timeout 设得太小)。 - 单靠 error_log 无法区分:是后端真慢,还是 Nginx worker 因资源不足(如
worker_connections耗尽)导致新连接排队等待,看起来像超时。
关键配套动作:让 error_log 发挥最大价值
启用
debug级别 error_log(临时)
它会输出每个连接的状态变迁(如http wait request,http proxy start,http proxy read header),可清晰看到 worker 卡在哪一阶段。注意:仅用于排查,生产慎用。-
结合
access_log中的$request_time和$upstream_response_time- 若
request_time ≈ upstream_response_time,说明耗时全在 upstream,事件循环只是被动等待 - 若
request_time > upstream_response_time且差值稳定(如总是多出 3~5s),可能是 Nginx 自身处理环节有阻塞(如大 body 解析、SSL 握手慢、log_format 中用了昂贵变量)
- 若
-
检查系统级指标
-
ss -s查看total: 12345和tcp:下的inuse/orphan数量 → 判断是否连接数压爆 -
cat /proc/nginx_pid/status | grep -i "voluntary_ctxt_switches\|nonvoluntary_ctxt_switches"→ 非自愿上下文切换高,说明 worker 频繁被内核抢占,可能 CPU 不足或锁争用
-
确认
worker_shutdown_timeout是否生效
若 reload 后旧 worker 持续运行超时(默认无限制),说明它卡在不可中断的系统调用中(如read()等待上游),这是事件循环退出路径被阻塞的明确信号。
不复杂但容易忽略


















