导读:本期聚焦于半糖创作的《Nginx如何记录请求中的DNS解析时间?配置与原理详解》,敬请观看详情。把后端域名交给Nginx做代理时,若上游使用域名而非IP,每次连接都可能触发DNS查询。想定位接口变慢是否由解析延迟引起,就得在日志里拿到解析耗时。Nginx的$upstream_response_time只覆盖连接和读取,并不单独拆出DNS阶段。其实借助核心模块的内置变量与resolver指令,能把解析开始与结束的差值写进访问日志。本文说明如何通过log_format扩展字段,配合valid参数和超时设置,稳定输出每条请求的DNS耗时,并解释变量生效条件和常见漏记原因。

在 Nginx 作为反向代理或七层负载均衡的场景中,许多配置会将 proxy_pass 的目标写成带域名的地址,例如 http://api.ipipp.com。此时 Nginx 在建立上游连接之前,必须先将域名解析为 IP 地址,这一过程就是 DNS 解析。如果解析本身耗时较长,用户感知到的请求延迟就会明显上升,但 Nginx 默认的访问日志并不会把这段时间单独体现出来。因此,想要排查这类延迟问题,就需要理解 Nginx 处理上游域名时的内部阶段,并利用它提供的变量与扩展能力,把解析耗时从整体请求时间中剥离并记录下来。

Nginx如何记录请求中的DNS解析时间?配置与原理详解

一、DNS解析的触发机制与可用的计时变量

Nginx 只有在配置里出现域名形式的上游地址,并且使用了 resolver 指令时,才会在运行时进行动态 DNS 解析。如果上游地址直接写成 IP,或者仅依赖系统静态 /etc/hosts 文件进行本地解析,那么 Nginx 层面根本不存在真正的 DNS 查询过程,自然也就无法从 Nginx 变量中获取解析耗时。因此,记录解析时间的前提是先确保配置中已经启用了 resolver,并且 proxy_pass 使用的是域名。

Nginx 核心模块提供了 $upstream_addr$upstream_connect_time$upstream_response_time 等变量,但它们分别表示上游地址、建立 TCP 连接耗时和接收响应耗时,并没有一个现成的 $dns_time 变量来直接输出 DNS 解析时间。要拿到解析耗时,需要借助组合变量和计算差值的方式间接实现。常见做法包括使用第三方模块 nginx-module-vts 查看细分指标,或者在生产环境使用 OpenResty 嵌入 Lua 脚本,通过 ngx.now() 在解析前后打点求差。

下面是一段在 OpenResty 中利用 Lua 记录 DNS 解析耗时的简化示例。示例中先记录开始时间,再通过 ngx.socket.tcp() 触发一次域名解析与连接,最后将耗时写入自定义变量。这里的耗时包含了 TCP 连接建立时间,仅用于说明打点思路;实际项目中可以结合 cosocket 的 DNS 解析接口进一步细化。

local function measure_dns_lookup(host, port)
    local sock = ngx.socket.tcp()
    local start_time = ngx.now()
    -- connect 会触发域名解析与 TCP 连接,此处以整体耗时近似表示解析阶段耗时
    local ok, err = sock:connect(host, port)
    local cost = ngx.now() - start_time
    sock:close()
    return ok, err, cost
end

local host = "api.ipipp.com"
local port = 80
local connected, err, dns_cost = measure_dns_lookup(host, port)
if not connected then
    ngx.log(ngx.ERR, "connect failed: ", err)
    dns_cost = 0
end
ngx.var.dns_cost = string.format("%.3f", dns_cost)

二、通过 log_format 输出解析耗时到访问日志

拿到解析耗时变量后,下一步就是将它写入访问日志。Nginx 的 log_format 指令允许组合任意变量,包括通过 set 指令定义的自定义变量。我们可以在 http 块中先声明一个变量,例如 $dns_cost,并为其设置默认值,再由 Lua 脚本或 map 指令赋值。随后在 log_format 中追加该字段,即可让每行访问日志都携带解析耗时。

配置时需要注意变量的作用域。自定义变量应当在 serverlocation 之外使用 set 初始化,否则 Lua 赋值时可能因为变量未创建而报错。另外,如果上游启用了 keepalive 长连接,连接复用后后续请求不会再触发 DNS 解析,此时 $dns_cost 应当保持为 0 或结合 $connection 复用标识来判断。下面给出一份典型的 nginx.conf 片段,展示格式定义与变量初始化。

http {
    log_format main "$remote_addr - $time_local "$request" "
                    "$status $body_bytes_sent "
                    "dns=$dns_cost";
    server {
        set $dns_cost "0";
        location / {
            access_by_lua_block {
                local start = ngx.now()
                -- 实际项目中应在此调用解析函数,计算得到耗时
                local cost = 0.012
                ngx.var.dns_cost = string.format("%.3f", cost)
            }
            proxy_pass http://api.ipipp.com;
            access_log /var/log/nginx/access.log main;
        }
    }
}

上述配置中,log_format 里的 dns=$dns_cost 字段会在每条日志末尾追加解析耗时。若某个请求没有走代理域名,或者命中了连接复用,则变量保持默认值 0。通过这种方式,运维人员可以使用 awk 或 ELK 等工具筛选 dns 值偏大的记录,快速锁定解析异常。同时,建议把 resolver 的超时时间设置得小一些,避免解析卡死时拖垮整体请求处理。

三、原生 Nginx 无 Lua 时的替代记录方案

并非所有环境都能安装 OpenResty 或 Lua 扩展。对于纯 Nginx 用户,虽然无法直接在应用层打点拿精确耗时,但仍有一些替补手段。一种方式是利用 error_log 的 debug 级别打开 resolver 相关日志,观察解析过程;这种方式适合开发环境或临时调试,不适合长期运行在生产环境。另一种思路是使用 split_clientsmap 做间接标记,把经历过重新解析或解析失败的请求单独打标,再在访问日志中用标志位反映出来。这种方式虽然拿不到精确毫秒数,但可以快速识别哪些请求产生了解析行为。

此外,商业版 Nginx Plus 内置了更为丰富的 $upstream 指标,其中包含解析阶段的细分数据。开源版则可以考虑在系统层使用 tcpdump 抓取 53 端口的 DNS 流量,离线计算查询耗时,再与 access_log 中的 $request_time 对齐分析。这种方法

这种方法虽然能获得完整的 DNS 查询序列,但需要额外的抓包权限和分析成本,适合在排查疑难解析问题时使用。实际生产环境中,建议优先考虑 OpenResty 或 Nginx Plus 内置指标,因为它们在应用层直接采集,侵入性小且易于接入现有监控。对于纯开源 Nginx,只能退而求其次,使用 debug 日志或 map 标记,再配合系统层抓包进行离线关联。

无论采用哪种方案,都应注意以下几点:第一,DNS 解析耗时指标不宜单独作为告警阈值,因为解析时间受网络抖动、缓存命中率、上游 DNS 服务器负载等多因素影响,需要结合 $request_time 和 $upstream_connect_time 一起判断。第二,resolver 配置中的 valid 参数与连接池复用会直接影响解析频率,调大 valid 可以降低解析次数,但可能在上游 IP 变更后出现短时间连接失败;调小 valid 能更快感知变更,但会增加解析开销。第三,如果使用 Lua 打点记录,注意变量作用域和连接复用的逻辑,避免把上一次解析耗时错误地记到当前请求上。

最后,建议把解析耗时作为访问日志的一个补充维度,而不是核心监控指标。通过 log_format 中预留 dns 字段,在生产中静默收集,当发现某类域名解析频繁超时或耗时异常时,再结合 resolver 日志与抓包进一步定位。这样既能控制日志体量,也能在问题发生时快速回溯。

至此,从 Lua 计时到纯 Nginx 的间接方案,再到系统层抓包对齐,我们已经覆盖了记录 Nginx DNS 解析耗时的主要路径。希望这些方法能帮助你在实际运维中更精准地把握 DNS 解析这一关键环节。

NginxDNS解析access_log修改时间:2026-08-13 12:57:36

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。