$connection 不能用于追踪单个长连接,因为它是 worker 进程内活跃连接的瞬时总数计数器,无唯一性、不跨请求保持、无法绑定状态码;应改用 $connection_requests 加自定义连接指纹(如 "$pid-$remote_addr:$remote_port-$msec")来识别同连接的全部请求。

直接用 $connection 变量无法绘制长连接生命周期内的状态码分布图——它不是连接 ID,而是当前工作进程内活跃连接的瞬时计数器,每次请求都重置、无唯一性、不跨请求保持,根本不能标识某条 TCP 连接的全生命周期。
为什么 $connection 不能用于追踪单个长连接
$connection 是 Nginx 内部维护的一个全局计数器(ngx_atomic_t 类型),表示当前 worker 进程中所有已建立但尚未关闭的 TCP 连接总数。它的值随 accept/close 动态增减,同一连接上多个请求会反复触发该值变化,且不同连接共享同一个计数器。它不具备连接粒度的可追溯性,也无法与具体请求的状态码(如 200/499/502)做绑定。
- 一次 keepalive 长连接可能承载 10 次 HTTP 请求,
$connection在这期间可能从 100 → 101 → 100 → 101 …… 反复波动,无法对应到第 3 次请求返回了 499 - 日志中每行记录一次请求,
$connection字段对同一连接的不同请求几乎必然不同,无法归并 - 它不区分连接归属,更不携带时间戳或序列号,无法构建“某连接发起 → 若干请求 → 最终关闭”的时序链路
真正可行的替代方案:用 $connection_requests + 自定义连接标识
要分析长连接生命周期内的状态码分布,关键不是记录连接本身,而是识别“属于同一条连接的所有请求”。Nginx 提供两个可靠抓手:
- $connection_requests:记录该连接已处理的请求数(从 1 开始递增),同一连接下各请求此值连续递增(如 1,2,3,…),是天然的请求序号
-
自定义连接 ID:通过
log_format结合$pid(worker 进程 ID)、$msec(毫秒级时间戳)、$remote_addr:$remote_port构造准唯一连接指纹(例如:"$pid-$remote_addr:$remote_port-$msec"),在连接首次请求时生成并复用
示例日志格式:
log_format conn_log '$time_iso8601 $pid-$remote_addr:$remote_port-$msec $connection_requests $status $request_time';数据采集与绘图流程
拿到含连接指纹和请求序号的日志后,按以下步骤聚合:
- 用 Logstash / Fluentd / 或 Python 脚本按连接指纹(如
"123-192.168.1.10:52183-1717023456.123")分组 - 每组内按
$connection_requests排序,提取全部$status - 统计每条连接的状态码序列(如 [200,200,499,200]),可进一步分类:是否含错误码、错误发生位置(首次?末次?中间?)、错误前后状态等
- 用 Grafana + Prometheus(预聚合指标)或 Python(Matplotlib/Seaborn)绘制热力图:横轴为请求序号(0~N),纵轴为连接样本,颜色深浅表示 2xx/4xx/5xx 出现频次
补充建议:配合 upstream 模块观测连接复用效果
若关注反向代理场景下的长连接行为,还需开启 upstream 日志变量:
-
$upstream_connect_time和$upstream_header_time可判断是否复用了上游连接(多次请求中前者仅首次非零) -
$upstream_addr若含逗号(如10.0.1.5:80,10.0.1.5:80),说明发生了连接复用;若每次不同,说明未复用 - 将这些字段加入日志,能交叉验证客户端长连接是否真正转化为后端连接复用,避免“假 keepalive”











