[坑] Nodejs中由 DNS 造成的 ETIMEDOUT 错误

发布于 1周前 作者 bupafengyu 来自 nodejs/Nestjs

[坑] Nodejs中由 DNS 造成的 ETIMEDOUT 错误
这几天碰到一个小坑,花了点时间调研了下,希望对后人有帮助
描述:
设想你有一个应用,对外暴露 2 个接口
http://www.my.com/api/77
http://www.my.com/api/88
他们内部各自会依赖第三方的 http 接口
接口 88 依赖: http://www.88.com/api,http 请求超时设置为 5S
接口 77 依赖: http://www.77.com/api,http 请求超时设置为 5S
假设你自己的应用所在的机器设置了一个 DNS SERVER,地址为 66.66.66.66,这台 DNS 更像一个代理,
他对 www.88.com 的解析需要依赖上游的 88.88.88.88 这台 DNS 服务
他对 www.77.com 的解析需要依赖上游的 77.77.77.77 这台 DNS 服务

如果这时候 77.77.77.77 这台 DNS 挂了会发生什么?情况如下

1. 在 66.66.66.66 这台代理 DNS 代理中,对 www.77.com,www.88.com 的记录缓存都还没过期时一切正常
2. 当 66 对 www.77.com 的记录过期了,那么所有外部对你的 www.my.com/api/77 的访问都将失败,因为你内部无法访问 www.77.com
3. 坑的地方来了, 当 66 对 www.88.com 的记录过期了,外部访问你的 www.my.com/api/88 会发生什么?答案是都有可能,既可能正常,也可能失败,原因如下:

正常的原因就不赘述了,因为 88.88.88.88 服务器依然坚挺,即使记录过期了 66.66.66.66 还是可以问 88.88.88.88 拿到 www.88.com 的 IP
那为什么会错误呢!因为 nodejs 的 runtime 默认使用了 4 个 worker 线程去处理 dns 解析请求。如果这 4 个线程都在处理对 77 的解析,那么这些线程都会 pending 在那边,直到 DNS 解析失败发生。(感受了一下好像是 10S,应该是 OS 的一个设置值)
那在 pending 期间所有的对 www.88.com 的请求也都会被 pending 起来(因为 88 此时也需要解析,他在 66 中的记录已经过期了)。又因为 http 请求超时设置的是 5S,最终就都会变成 ETIMEDOUT 或者 ESOCKETTIMEDOUT

此外,该问题还可能引发 v8 out of memory 的问题,原本一个请求花费 50MS,那么他占用的内存在 50MS 后就可以被 GC,但是当这个时间变成 5S 后,他占用的内存也要在 5S 后才能被 GC。如果 5S 内量很大,就有可能造成 OOM


5 回复

我可以说我不明白但觉得很厉害吗?
最近在玩本地 DNS 帆樯


重点是,“ nodejs 的 runtime 默认使用了 4 个 worker 线程去处理 dns 解析请求”,为什么只有 4 个。

一般无脑装了个 nscd 缓存 DNS,其他倒是没怎么关注

nodejs 用的线程池是由 libuv 库提供的,libuv 默认设置的就是 4,这个配置可修改.
通过环境变量 UV_THREADPOOL_SIZE 来修改,最大 128

在Node.js中,遇到由DNS解析造成的ETIMEDOUT错误通常表示DNS查询请求在指定的时间内没有得到响应。这种问题可能由多种原因引起,包括网络延迟、DNS服务器故障或配置错误等。

解决步骤

  1. 检查网络连接: 确保你的服务器或开发机器能够正常访问外部网络。

  2. 配置备用DNS服务器: 使用公共DNS服务器(如Google的8.8.8.8和8.8.4.4)来替换默认的DNS服务器,这有时能解决DNS解析问题。

    const dns = require('dns');
    dns.setServers(['8.8.8.8', '8.8.4.4']);
    
  3. 增加DNS解析超时时间: Node.js的dns模块默认的超时时间可能不足以应对某些网络条件。你可以通过dns.resolvedns.resolve4等方法手动设置超时时间。

    dns.resolve4('example.com', { timeout: 10000 }, (err, addresses) => {
      if (err) {
        console.error('DNS resolution error:', err);
      } else {
        console.log('Addresses:', addresses);
      }
    });
    
  4. 错误处理: 确保你的代码能够妥善处理ETIMEDOUT错误,可能通过重试机制或回退逻辑来提高应用的健壮性。

  5. 日志和监控: 增加DNS查询的日志记录,以便在出现问题时能够快速定位和分析原因。

通过以上步骤,你可以有效减少由DNS造成的ETIMEDOUT错误,提升Node.js应用的稳定性和可靠性。

回到顶部