
知识是人生的灯塔,只有不断学习,才能照亮前行的道路
📢 大家好,我是 WeiyiGeek,一名深耕安全运维开发(SecOpsDev)领域的技术从业者,致力于探索DevOps与安全的融合(DevSecOps),自动化运维工具开发与实践,企业网络安全防护,欢迎各位道友一起学习交流、一起进步 🚀,若此文对你有帮助,一定记得点个关注⭐与小红星❤️或加入到作者知识星球『 全栈工程师修炼指南』,转发收藏学习不迷路 😋 。
温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_713d6lwet0zc.html
针对 error.log 使用内存环形缓冲区及提高日志级别
在 Nginx 中,默认情况下 error.log 日志级别为 warn,这意味着只有警告及以上级别的日志会被记录,实际上在大多数场景中都是足够的。但如果需要记录更详细的错误信息,比如调试或监控目的,可以将日志级别设置为 info 或更低debug(仅限于测试),另外对于 error.log 日志为了提高性能和减少磁盘IO,减少无意义静态资源访问的错误日志以及使用内存环形缓冲区(buffer)来存储日志信息,若于留存的需要请一定设置日志轮转避免日志文件过大,在容器中通常会输出到控制台上,大家可根据实际场景选择合适的日志级别和配置。

指令参数:
- •
error_log指令用于指定错误日志的路径和级别。默认情况下,Nginx 会将错误信息写入到logs/error.log文件中,并且记录警告及以上级别的日志,另外,对于error_log /path/to/log debug;级别的日志需要在 Nginx 构建时使用--with-debug,可通过nginx -V命令查看是否已经加入此参数。
go
Syntax: error_log file [level];
Default: error_log logs/error.log error;
Context: main, http, mail, stream, server, location
# 日志级别(从低到高,从详细到精简)
debug, info, notice, warn, error, crit, alert, or emerg
# 示例演示
# 将日志级别设置为 warn,并将日志输出到 /var/log/nginx/vhost/www.log 文件。
error_log /var/log/nginx/vhost/www.log warn;
# 将 info 日志级别的错误日志,通过 syslog 方式输出到远程日志服务器。
error_log syslog:server=192.168.1.100:514,facility=local7,tag=nginx_error info;
# 将错误日志发送到容器内的 /dev/log (通常由 journald 监听)
error_log syslog:server=unix:/dev/log,nohostname;
# 使用内存环形缓冲区,例如:32MB 大小。
error_log memory:32m debug;
- •
log_not_found指令用于控制是否记录找不到文件的错误。默认情况下,Nginx 会将这类错误写入到error.log日志文件中。如果不需要这些日志信息,可以将其关闭以减少磁盘 I/O 压力和日志文件的大小。
go
Syntax: log_not_found on | off;
Default: log_not_found on;
Context: http, server, location
# 使用示例
# 匹配 favicon.ico 请求时不记录日志,减少磁盘 I/O 压力。
location ~* \.(ico)$ {
# 1. 禁用访问日志记录
access_log off;
# 2. 禁用错误日志记录(防止找不到文件时报错)
log_not_found off;
# 4. 设置长期的客户端缓存,减少浏览器重复请求频率
expires 30d;
}
- •
debug_connection指令用于在调试模式下,仅记录来自指定 IP 地址/段/unix的连接的相关日志。这对于排查特定客户端或服务器的网络问题非常有用。默认情况下,此功能是关闭的。
go
Syntax: debug_connection address | CIDR | unix:;
Default: ---
Context: events
# 示例演示
events {
debug_connection 127.0.0.1;
debug_connection localhost;
debug_connection 192.0.2.0/24;
debug_connection ::1;
debug_connection 2001:0db8::/32;
debug_connection unix:;
...
}
error_log /path/to/log;
- •
debug_points指令用于在调试模式下检测内部错误,例如在重新启动工作进程时出现套接字泄漏,启用调试点会导致生成核心文件(中止)或停止进程(停止),以便使用系统调试器进行进一步分析。
go
Syntax: debug_points abort | stop;
Default: ---
Context: main
另外,对于 debug 级别的日志,若需要在生产环境中做异常定位,不建议直接输出到文件可能导致磁盘IO激增,引发性能问题,所以建议使用内存环形缓冲区(循环写)规避高负载,下面作者将演示如何配置及使用内存环形缓冲区。
首先,在编译 Nginx 时需要加上 --with-debug 参数,如下所示。
go
cd /usr/local/src/nginx-1.29.0/
# 编译安装 Nginx,并开启 debug 模式
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --sbin-path=/usr/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --error-log-path=/var/log/nginx/logs/error.log --http-log-path=/var/log/nginx/logs/access.log --lock-path=/var/run/nginx.lock --modules-path=/usr/local/nginx/modules --with-http_stub_status_module --with-http_realip_module --with-http_v2_module --with-http_ssl_module --with-http_slice_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt=-O2 --with-compat --with-debug
make -j$(nproc) && make install
# 验证
nginx -V 2>&1 | grep -- "--with-debug"
其次,在测试的 test.conf 配置文件中,将 error_log 日志级别设置为 debug 并使用内存环形缓冲区(例如:32MB),意味者只能访问最近的 32MB 日志信息,如下所示。
go
tee test.conf <<'EOF'
http {
# 为了调试方便,我将worker 进程数量设置为 1
worker_processes 1;
# 关键点:使用内存环形缓冲区
error_log memory:32m debug;
...
# 虚拟主机配置,反向代理到上游服务器组
server {
listen 80;
server_name test.weiyigeek.top;
charset utf-8;
default_type text/html;
# 启用 HTTP/2 支持
http2 on;
# 日志文件
access_log /var/log/nginx/test.log main;
location / {
proxy_pass http://backend_server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 启用缓存,指定缓存区域为 data01
proxy_cache data01;
# 自定义缓存KEY
proxy_cache_key $scheme$request_method$host$uri;
# 响应码为200的响应数据进行缓存有效期为5分钟
proxy_cache_valid 200 5m;
# 自定义响应头,用于验证上游缓存状态
add_header X-Cache-Status $upstream_cache_status;
}
}
EOF
之后,重启 Nginx 并使用 gdb 命令查看转存内存中日志,如下所示。
go
# 重启 Nginx 服务
nginx -s reload
# 安装 gdb 调试工具
dnf install gdb -y
tee nginx-memory-log.gdb <<'EOF'
# 显式强制转换,防止偏移计算错误
set$log = (ngx_log_t *) ngx_cycle->log
set$found = 0
while$log != 0
# 检查 writer 是否匹配内存写入器
if$log->writer == &ngx_log_memory_writer
set$found = 1
loop_break
end
set$log = $log->next
end
if$found == 1
set$buf = (ngx_log_memory_buf_t *) $log->wdata
printf"找到内存日志: Start=%p, End=%p, Size=%d 字节\n", $buf->start, $buf->end, $buf->end - $buf->start
dump binary memory debug_log.txt $buf->start $buf->end
else
printf"错误: 在当前 Nginx 运行实例中未发现 memory 类型的 error_log。\n"
printf"请检查 nginx.conf 是否包含: error_log memory:32m debug;\n"
end
EOF
# 查看 Nginx Worker 进程的 PID
$ ps -ef | grep "nginx"
root 3089982 1 0 16:57 ? 00:00:00 nginx: master process nginx
nginx 3090687 3089982 0 17:01 ? 00:00:00 nginx: worker process
root 3090709 3071798 0 17:01 pts/0 00:00:00 grep --color=auto nginx
# 使用 gdb 调试 Nginx,并查看内存中的日志
gdb -p 3090687 -ex "source nginx-memory-log.gdb" --batch
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib64/libthread_db.so.1".
0x00007ff0bf72b517 in epoll_wait () from /usr/lib64/libc.so.6
找到内存日志: Start=0x7ff0ba3fe010, End=0x7ff0bc3fe010, Size=33554432 字节
[Inferior 1 (process 3090687) detached]
# 成功后将生成 debug_log.txt 文件,即为内存环形缓冲区中的日志内容。
$ ls -alh debug_log.txt
-rw-r--r-- 1 root root 32M Mar 12 17:03 debug_log.txt

weiyigeek.top-使用gdb输出nginx内存环形缓冲区图
使用 sendfile 零拷贝技术提升性能
什么是 sendfile 零拷贝技术呢?
简单来说,就是在文件传输过程中,避免用户空间与内核空间的频繁数据拷贝,以及减少进程间切换操作,从而减少CPU的负担和提升性能,在 Linux 系统中,sendfile 系统调用是实现这一技术的关键。
例如,左图中应用程序(如 NGINX)作为静态资源服务,需从磁盘读取文件并发送至客户端,其流程是程序调用 read() 将磁盘内容读入应用程序即文件经磁盘 → 高速缓冲区 → 应用程序内存,然后调用 write() 在 TCP socket 上发送数据即 内核 Socket 缓冲区 → TCP 滑动窗口机制 → 网卡 → 客户端。
而右图使用了 sendfile 零拷贝技术,它首先会调用 sendfile(),指定源文件、起始偏移、字节数、目标 socket 缓冲区(文件句柄),即内核直接通过 DMA 将磁盘数据读入内核页缓存,并直接送入 socket 缓冲区(跳过用户态)。其拷贝次数减至两次(磁盘→页缓存→socket缓冲区),避免用户态内存拷贝,减少上下文切换次数,在此过程应用程序无需参与数据搬运。

weiyigeek.top-sendfile 零拷贝技术图
指令参数:
- •
sendfile: 开启或关闭 sendfile 功能, 默认情况下,sendfile 是禁用的(off)。
go
Syntax: sendfile on | off;
Default: sendfile off;
Context:http, server, location, ifin location
# 示例演示
location /video/ {
sendfile on;
tcp_nopush on;
aio on;
}
- •
sendfile_max_chunk: 限制单个 sendfile() 调用中可以传输的数据量,如果没有限制,一个快速连接可能会完全占用工作进程。
go
Syntax: sendfile_max_chunk size;
Default: sendfile_max_chunk 2m;
Context: http, server, location
温馨提示: 从 nginx 0.8.12 和 FreeBSD 5.2.1 开始,aio 可用于预加载 sendfile() 的数据:
温馨提示:在启用 O_DIRECT(direct IO)时,sendfile 将会被自动被禁用,因为当文件过大时,sendfile 可能会导致内核缓冲区溢出,所以 directio 更适合大文件传输场景。
go
location /video/ {
sendfile on;
aio on;
directio 8m;
}
另外,还有一个问题当我们使用 sendfile 时,若需对响应体(response body)进行 gzip 压缩,则必须将原始文件从磁盘读入用户态内存处理(依赖 output_filter 阶段),此时 sendfile 零拷贝失效, 为此设计专用模块gzip_static 模块。
ngx_http_gzip_static_module 模块允许发送扩展名为 ".gz" 的预压缩文件,而不是常规文件;模块为 content 阶段前置模块,比较考前,它会先对静态文件预先执行压缩命令,在原目录下生成同名 .gz 文件,若 .gz 文件存在且功能已开启,则直接返回该压缩文件内容,而非原始文件,并自动添加 Content-Encoding: gzip 及相应 Content-Type 等头部。默认情况下,此模块不是内置的,应使用--with-http_gzip_static_module 配置参数启用它。
指令参数:
go
Syntax: gzip_static on | off | always;
Default: gzip_static off;
Context: http, server, location
# 参数
on : 检查客户端 Accept-Encoding 请求头;仅当客户端声明支持 gzip 时才返回 .gz 文件;否则返回原始文件。
always : 无论客户端是否声明支持 gzip,都返回 .gz 文件。
# 示例
gzip_static on;
gzip_proxied expired no-cache no-store private auth;
温馨提示:若仅存在 .gz 文件而无原始文件,always 模式仍可正常返回。
此外,若客户端不支持 gzip 压缩,但服务器端存在 .gz 文件,此时应如何处理?我们可使用 ngx_http_gunzip_module 模块,它是一个过滤器,它能够在客户端不支持 gzip 时自动解压 .gz 文件并返回原始内容,当需要存储压缩数据以节省空间和降低I/O成本时,该模块将非常有用。同样该模块默认不是内置的,需要通过--with-http_gunzip_module 配置参数启用。
指令参数:
- •
gunzip: 为缺乏gzip支持的客户端启用或禁用gzip压缩响应的解压缩。如果启用,在确定客户端是否支持 gzip 时,还会考虑以下指令:gzip_http_version、gzip_proxied和gzip_disable。
go
Syntax: gunzip on | off;
Default:
gunzip off;
Context: http, server, location
- •
gunzip_buffers:设置用于解压缩响应的缓冲区的数量和大小。默认情况下,缓冲区大小等于一个内存页,这是4K或8K,具体取决于平台。
go
Syntax: gunzip_buffers number size;
Default: gunzip_buffers 32 4k|16 8k;
Context: http, server, location
本小节中讲解了在小文件静态资源网站时,启用 sendfile 零拷贝显著降低性能消耗,另外,针对于大文件传输以及资源压缩时 sendfile 将会被禁用,此时还需考虑启用 directio 直写磁盘,以及在需要压缩时,如何选择合适的模块(gzip_static 或 gunzip)来优化性能,
0x03 总结回顾
本篇文章,围绕前述四大软件优化方向(CPU、内存、磁盘IO、网络带宽)展开具体策略详解、实践,通过介绍主要的内核参数以及关键的 nginx 指令,帮助道友们在实际的运维工作中,能够应对不同场景下的优化需求,进而系统性地提升 Nginx 的性能。另外,也有助于道友们对 Nginx 的工作原理(机制)有更深的理解,从而在实际工作中能够更加灵活地应对各种问题。
最后有需要新手朋友们,可加入到作者知识星球中,完整系统的学习 Nginx 高性能Web服务器从入门到生产时间,后续也将持续更新 OpenResty 系列文章,感谢大家支持。
Nginx \| 核心知识150讲之基础介绍与进程信号管理学习笔记\](https://articles.zsxq.com/id_1b7rrwwqo422.html) \[Nginx \| 核心知识150讲之事件驱动框架笔记\](https://articles.zsxq.com/id_w52o5cd47zwi.html) \[Nginx \| 核心知识150讲之静态、动态模块编译使用笔记\](https://articles.zsxq.com/id_dertjh0239h3.html) \[Nginx \| 核心知识150讲之连接池与内存池介绍笔记\](https://articles.zsxq.com/id_wxgg5glpimm9.html) \[Nginx \| 核心知识150讲之常用容器类型介绍笔记\](https://articles.zsxq.com/id_xnch5n50hm9j.html) \[Nginx \| 核心知识150讲之HTTP模块指令处理流程介绍笔记\](https://articles.zsxq.com/id_4wfw412wei48.html) \[Nginx \| 核心知识150讲之处理请求的11个阶段与对应模块介绍笔记(上篇)\](https://articles.zsxq.com/id_70wxcdwid00g.html) \[Nginx \| 核心知识150讲之处理请求的11个阶段与对应模块介绍笔记(下篇)\](https://articles.zsxq.com/id_3dcvx6w81906.html) \[Nginx \| 核心知识150讲之变量介绍与实践应用笔记\](https://articles.zsxq.com/id_rx229kjix356.html) \[Nginx \| 核心知识150讲之反向代理流程与负载均衡实践笔记\](https://articles.zsxq.com/id_a2l6pacq0flr.html) \[Nginx \| 核心知识150讲之HTTP协议中反向代理学习实践笔记\](https://articles.zsxq.com/id_fkims9wlo8ww.html) \[Nginx \| 核心知识150讲之SSL证书签发与HTTPS加密传输学习实践笔记\](https://articles.zsxq.com/id_wwnjk3rmwroa.html) \[Nginx \| 核心知识150讲之前端静态缓存、反向代理动态缓存学习实践笔记\](https://articles.zsxq.com/id_6hpaidhf5l5s.html) \[Nginx \| 核心知识150讲之其它HTTP框架下反向代理学习实践笔记\](https://articles.zsxq.com/id_7wc0af7viwqr.html) \[Nginx \| 核心知识150讲之利用 open_file_cache 缓存文件句柄提升性能\](https://articles.zsxq.com/id_19oatlbj0w64.html) \[Nginx \| 核心知识150讲之介绍使用 HTTP/2.0 协议实现资源主动推送\](https://articles.zsxq.com/id_fib851xw86fq.html) \[Nginx \| 核心知识150讲之四层反向代理的七个阶段及对应模块浅析实践(上)\](https://articles.zsxq.com/id_wtv12vsjglt0.html) \[Nginx \| 核心知识150讲之四层反向代理的七个阶段及对应模块浅析实践(下)\](https://articles.zsxq.com/id_t5z28zcqv7us.html) \[Nginx \| 核心知识150讲之CPU层面性能优化指南\](https://articles.zsxq.com/id_c5gt26jx4zcp.html) \[Nginx \| 核心知识150讲之内存层面性能优化指南\](https://articles.zsxq.com/id_i1qqni3rce3f.html) \[Nginx \| 核心知识150讲之网络层面性能优化指南\](https://articles.zsxq.com/id_q8wr9cv6ke27.html) \[Nginx \| 核心知识150讲之磁盘IO层面性能优化指南\](https://articles.zsxq.com/id_713d6lwet0zc.html) \[Nginx \| 核心知识150讲之利用 stub_status 实时监控网络连接情况
(https://articles.zsxq.com/id_h0irm4e7kezu.html)

END
加入:作者【全栈工程师修炼指南】知识星球
『 全栈工程师修炼指南』星球,主要涉及全栈工程师(Full Stack Development)实践文章,持续更新包括但不限于企业SecDevOps和网络安全等保合规、安全渗透测试、编程开发、云原生(Cloud Native)、物联网工业控制(IOT)、人工智能Ai,从业书籍笔记,人生职场认识等方面资料或文章。
Q: 加入作者【全栈工程师修炼指南】星球后有啥好处?
✅ 将获得作者最新工作学习实践文章以及网盘资源。
✅ 将获得作者珍藏多年的全栈学习笔记(需连续两年及以上老星球友,也可单次购买)
✅ 将获得作者专门答疑学习交流群,解决在工作学习中的问题。
✅ 将获得作者远程支持(在作者能力范围内且合规)。

获取:作者工作学习全栈笔记
作者整理了10年的工作学习笔记(涉及网络、安全、运维、开发),需要学习实践笔记的看友,可添加作者微信或者回复【工作学习实践笔记】,当前价格¥299,除了获得从业笔记的同时还可进行问题答疑以及每月远程技术支持,希望大家多多支持,收获定大于付出!

知识推荐 往期文章
若文章对你有帮助,请将它转发给更多的看友,若有疑问的小伙伴,可在评论区留言你想法哟 💬!
