无感知日志滚动的关键是nginx -s reopen触发主进程重开日志句柄并让工作进程平滑处理存量连接,而非仅调大keepalive_requests;需配合logrotate的postrotate脚本执行reopen、禁用copytruncate,并通过lsof和tail验证文件句柄切换与写入分离。

要实现无感知的日志滚动,关键不是单纯调大 keepalive_requests,而是让 Nginx 在日志轮转(log_rotate)时,不中断长连接、不丢请求、不触发 502/503。核心在于:Nginx 主进程重载日志文件句柄 + 工作进程平滑处理存量连接。
理解 keepalive_requests 的真实作用
keepalive_requests 控制单个长连接最多处理多少个请求后主动关闭。它本身不决定日志是否“无感知”,但会影响轮转窗口内活跃连接的生命周期:
- 设得太小(如 10),连接频繁断开重连,轮转期间大量新连接会写入新日志,旧连接残留写入旧日志——导致日志分散、统计不准;
- 设得过大(如 10000),配合合理超时(
keepalive_timeout),可让多数连接在轮转前自然结束或稳定处于空闲态,降低“跨轮转写日志”的概率; - 它不能阻止工作进程继续向已打开的旧日志文件写入——这点必须靠
kill -USR1或nginx -s reopen触发主进程重新打开日志文件。
log_rotate 的正确触发方式
Linux 下常用 logrotate,但默认只做文件重命名和创建新空文件,Nginx 工作进程仍持有旧文件句柄(inotify 不感知),继续往旧文件写——这就不是“无感知”,而是“看不见的错位”。正确做法是:
- 在 logrotate 的
postrotate脚本中执行:
nginx -s reopen(推荐)或
kill -USR1 `cat /var/run/nginx.pid`; - 确保 logrotate 配置中
copytruncate不要开启——它会截断原文件,导致正在写的 worker 进程写入乱码或失败; - 验证是否生效:轮转后运行
lsof -p $(cat /var/run/nginx.pid) | grep "log",应看到旧日志文件状态变为deleted(实际已被 rename),新日志为REG且被主进程打开。
配合 keepalive_requests 的实用配置建议
目标是让轮转发生时,绝大多数活跃连接已完成请求或处于空闲等待状态,避免一个连接横跨新旧日志文件:
-
keepalive_requests 1000;—— 平衡复用与可控性,避免单连接存活过久; -
keepalive_timeout 60s;—— 空闲连接 60 秒后自动关闭,比典型 logrotate 周期(如每天 02:00)更可控; - 若业务有明确长轮询(如 SSE),可单独用
location降低该路径的keepalive_requests,避免拖慢整体日志收敛; - 搭配
log_format加入$connection_requests字段,便于事后分析某连接是否跨了轮转周期。
验证无感知的关键检查点
完成配置后,需实测确认是否真正“无感知”:
- 轮转前后连续发起 HTTP 请求(含 keep-alive 头),检查响应码始终为 200,无 502/503;
- 用
tail -f access.log和tail -f access.log.1同时观察,确认新请求只写入新文件,旧连接结束后不再向旧文件追加; - 查看 Nginx error.log,确认无
open() "/var/log/nginx/access.log" failed (2: No such file or directory)类报错——说明 reopen 成功; - 监控
nginx_stub_status中的Reading/Writing数值,轮转瞬间不应剧烈抖动。


















