Nginx 日志分析与性能调优:log_format 到 worker 调优

深入讲解日志分析与性能调优。涵盖 log_format、JSON 日志、error_log 排错、访问分析、worker 与 keepalive 调优。

日志是 Nginx 运维的"眼睛":缓存命中率、接口耗时、状态码分布、爬虫与攻击特征,全藏在访问日志里;而 error_log 里的告警则是排错的第一线索。性能调优则要求把 worker 数量、事件模型、keepalive 与缓冲参数调到与业务流量匹配。本文先讲 log_format 与 JSON 结构化日志,再讲日志分析,最后落到 worker/keepalive/缓冲的参数调优与压测方法,形成"观测 → 定位 → 调优"的完整闭环。

核心认知:先有可解析的日志,才有可靠的性能判断。调优必须基于日志指标而非直觉——每改一个参数,都要有日志数据来验证收益。

1. log_format 与日志规范

1.1 自定义日志格式

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for" '
                'rt=$request_time uct=$upstream_connect_time '
                'urt=$upstream_response_time';
变量含义
$request_time网关处理请求总耗时
$upstream_connect_time与上游建立连接耗时
$upstream_response_time上游响应耗时
$upstream_cache_status缓存命中状态
$status响应状态码

1.2 按站点分文件

http {
    log_format main '...';
    server {
        access_log /var/log/nginx/example.com.access.log main;
    }
}

避坑:$upstream_response_time 是逗号分隔的多值(多次重试会有多个),统计时需要按最后一个值处理,避免误判上游耗时。

2. JSON 结构化日志

2.1 定义 JSON 格式

log_format json_combined escape=json
    '{'
      '"time_local":"$time_local",'
      '"remote_addr":"$remote_addr",'
      '"request":"$request",'
      '"status":$status,'
      '"body_bytes_sent":$body_bytes_sent,'
      '"request_time":$request_time,'
      '"upstream_response_time":"$upstream_response_time",'
      '"http_user_agent":"$http_user_agent",'
      '"http_x_forwarded_for":"$http_x_forwarded_for",'
      '"cache_status":"$upstream_cache_status"'
    '}';

access_log /var/log/nginx/access.json json_combined;

2.2 采集到日志平台

# Filebeat 采集 JSON 日志 → Elasticsearch/Loki
filebeat.inputs:
  - type: log
    paths: [/var/log/nginx/access.json]
    json.keys_under_root: true
平台用途
ELK全文检索与可视化
Loki轻量日志聚合
ClickHouse大规模日志分析
自定义管道消费 JSON 做指标统计

2.3 JSON 日志查询示例

-- 以 ClickHouse 为例:查询慢接口与命中率
SELECT request,
       quantile(0.95)(request_time) AS p95,
       countIf(status >= 500) AS err_cnt,
       countIf(cache_status = 'HIT') AS hits,
       count() AS total
FROM nginx_access
WHERE time_local >= now() - INTERVAL 1 HOUR
GROUP BY request
ORDER BY p95 DESC
LIMIT 10;

核心认知:结构化日志让"日志"变成"数据"。带上 request_time、status、cache_status 等字段后,可以直接用 SQL/查询语言做耗时与命中率分析,而非靠正则硬啃文本。

3. 访问日志分析

3.1 常用统计命令

# 状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

# 请求耗时 Top10
awk '{print $NF, $7}' /var/log/nginx/access.log \
  | sort -rn | head -10

# 5xx 高频接口
awk '$9>=500 {print $7}' /var/log/nginx/access.log \
  | sort | uniq -c | sort -rn | head

3.2 goaccess 可视化

goaccess /var/log/nginx/access.log --log-format=COMBINED -o report.html
指标用途
状态码分布快速发现 5xx 异常
请求耗时定位慢接口
来源 IP 分布发现异常流量
命中率验证缓存策略

避坑:分析时务必按时间窗口聚合,避免把"活动高峰"误判为"异常暴涨"。建议对 5xx 与耗时设置基线,超过基线再告警。

4. error_log 与排错

4.1 日志级别

error_log /var/log/nginx/error.log warn;   # 生产常用 warn
# 排错时临时降到 debug,会有大量输出
error_log /var/log/nginx/error.log debug;
级别场景
error仅错误
warn生产推荐(含 warn 及以上)
notice含配置通知
info/debug仅排错,勿长期开启

4.2 典型错误解读

error_log 关键字含义处理
upstream timed out上游超时调大 proxy_read_timeout
no resolver defined缺 resolver为变量 proxy_pass 配 resolver
worker_connections are not enough连接不足提高 worker_connections
recv() failed客户端异常断开检查超时与限流

核心认知:error_log 出现 worker_connections are not enough 是连接容量告急的信号,比 5xx 更早暴露容量瓶颈。

5. worker 与事件模型调优

5.1 worker 数量

worker_processes auto;               # 通常等于 CPU 核数
worker_cpu_affinity auto;            # 绑定 CPU,减少迁移
worker_rlimit_nofile 65535;          # 文件描述符上限
场景worker_processes
CPU 密集与核数相同
IO 密集/大量连接核数或核数 × 1.5
容器/低配1~2 即可

5.2 事件与连接参数

events {
    worker_connections  4096;
    use                 epoll;
    multi_accept        on;
    accept_mutex        on;
}

避坑:worker_rlimit_nofile 必须与系统 ulimit -n 配套。连接上限 = worker_processes × worker_connections,同时受文件描述符约束,三者需一起核算。

6. keepalive 与缓冲调优

6.1 keepalive 参数

server {
    keepalive_timeout  30;       # 客户端空闲连接保持
    keepalive_requests 1000;     # 单连接最多处理请求数
    keepalive_timeout 30s;       # 新版区分长短连接
}

upstream backend {
    server 10.0.0.11:8080;
    keepalive 32;                # 上游空闲连接池
}

6.2 缓冲与文件处理

sendfile on;                     # 内核态直接发送文件
tcp_nopush on;                   # 攒满包再发,配合 sendfile
tcp_nodelay on;                  # 小包即时发送,配合 keepalive
proxy_buffering on;              # 上游响应缓冲
client_body_buffer_size 128k;
参数作用场景
sendfile减少拷贝静态文件
tcp_nopush减少小包大响应
tcp_nodelay降低延迟小响应/交互
proxy_buffering缓冲上游响应常规代理;SSE 需 off

避坑:SSE/流式接口必须 proxy_buffering off,否则会被缓冲导致客户端收不到实时推送;静态大文件则用 sendfile + tcp_nopush 提升吞吐。

7. 压测与瓶颈定位

7.1 压测工具

# ab 压测
ab -n 100000 -c 100 http://example.com/api/user

# wrk 多线程压测
wrk -t 8 -c 400 -d 30s --latency http://example.com/

# 观察系统指标
vmstat 1

7.2 指标解读

指标健康值异常信号
请求耗时 p95< 100ms缓慢上升
错误率< 1%5xx 突增
worker 连接数< 80% 上限接近上限告警
CPU多核均衡单核打满
带宽有富余逼近峰值

7.3 调优流程

观察日志指标 → 定位瓶颈(CPU/连接/带宽/后端)
→ 针对性改参数 → 复测对比 → 记录基线
瓶颈对应调优
连接数打满提高 worker_connections / 开 keepalive
单核打满检查 worker_cpu_affinity 与负载均衡
上游慢缓存 + 限流 + 调大 read 超时
带宽高gzip/Brotli 压缩

核心认知:压测必须分阶段——先压静态、再压动态、再压代理链路,逐层定位瓶颈落在 Nginx、网络还是后端。任何参数修改都要保留前后基线做对比。

8. 总结

环节要点
日志格式log_format 记录 request_time/upstream/cache 字段
结构化JSON 日志直接进日志平台分析
分析awk 统计状态码/耗时,goaccess 可视化
error_logwarn 级别,按关键字排错
worker与核数匹配,rlimit 配套
keepalive客户端与上游双端配置
缓冲sendfile/tcp_nopush/proxy_buffering 按场景
压测分阶段压测,保留基线对比

日志让 Nginx 的行为透明,调优让它的性能与流量匹配。把日志指标接入告警、把压测基线沉淀为文档,网关层就能持续演进。本专题六篇文章至此形成一个完整的 Nginx 网关与 Web 服务知识体系,从配置结构到代理、加密、缓存、安全再到观测,环环相扣。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「nginx」更多文章

  1. Nginx Ingress Controller:Kubernetes 云原生网关的路由、证书与金丝雀发布
  2. Nginx 监控与可观测性:stub_status 指标、Prometheus 集成与容量规划
  3. Nginx 静态资源与页面加速:零拷贝、缓存验证与 Brotli 压缩联动