nginx 通过异步非阻塞方式将 dns 查询作为 i/o 事件纳入事件循环,依赖线程池或 resolver 封装发起查询,超时由定时器事件控制,结果通过回调注入,配合 resolver、resolver_timeout 和 valid 参数实现不阻塞、可缓存、可重试的动态解析。

Nginx 的事件驱动模型本身不直接执行 DNS 解析,它通过异步、非阻塞方式调度并等待系统完成 DNS 查询,再将结果交由事件循环继续处理。真正做解析的是底层操作系统(如 getaddrinfo)或内核机制(如 epoll + 线程池),Nginx 只负责“不卡住、不等死、不乱序”。
Nginx 中 DNS 解析的触发场景
常见于以下配置中:
-
proxy_pass http://example.com(域名未硬编码为 IP) -
fastcgi_pass backend.example.com -
upstream块中使用域名定义服务器 -
resolver指令启用后,所有动态域名代理都依赖它
此时 Nginx 必须在运行时把域名转成 IP,但不能阻塞 worker 进程——否则一个慢 DNS 就拖垮整个并发能力。
DNS 请求如何融入事件驱动流程
Nginx 把 DNS 查询当作一种“I/O 事件”来管理,具体分三步:
发起查询不阻塞
调用getaddrinfo()时,Nginx 将其提交到内部线程池(默认thread pool或resolver自带的异步封装),立即返回,继续处理其他请求。结果通过事件通知回调
解析完成后,线程池唤醒对应 worker 的事件循环,触发预注册的回调函数(如ngx_resolver_handler),把 IP 地址填入缓存或更新 upstream 状态。超时与重试由事件机制兜底
resolver_timeout 5s并非简单 sleep,而是设置一个定时器事件;到期未响应就触发失败回调,跳过该节点或尝试 fallback。
关键配置影响事件行为
这些指令直接决定 DNS 在事件模型中的表现:
resolver 114.114.114.114 valid=30s;
指定 DNS 服务器,并启用缓存(30 秒内相同域名不再发请求)resolver_timeout 10s;
单次查询最大等待时间,超时后走错误路径,不影响其他连接proxy_set_header Host $host;配合proxy_pass http://$host使用时,每次请求都可能触发新解析(慎用,易放大 DNS 压力)valid=参数配合resolver缓存,减少重复事件调度,降低线程池负载
实际瓶颈与优化点
DNS 不是纯 CPU 任务,但会暴露事件模型的协作短板:
-
线程池过小 → 查询排队 → 表现像阻塞
默认thread pool仅 32 个线程,高并发动态域名代理下建议调大:thread_pool default threads=64 max_queue=65536;
-
DNS 服务器响应慢 → 大量超时事件堆积
推荐配置多个resolver(支持轮询):resolver 114.114.114.114 223.5.5.5 valid=30s;
频繁解析同一域名 → 缓存未生效
确保valid=时间合理,且proxy_pass中不要混用$host和硬编码 IP,避免绕过缓存逻辑
Nginx 的事件驱动不是靠自己实现 DNS 协议,而是把系统级异步能力“接进”自己的事件循环,让 DNS 成为可调度、可超时、可缓存的一环,而不是一个黑盒阻塞点。











