[坑] 由 DNS 造成的 ETIMEDOUT 错误

2017-05-18 20:19:59 +08:00
 netssfy
这几天碰到一个小坑,花了点时间调研了下,希望对后人有帮助
描述:
设想你有一个应用,对外暴露 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.comwww.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
2126 次点击
所在节点    Node.js
4 条回复
seasstyle
2017-05-19 10:02:09 +08:00
我可以说我不明白但觉得很厉害吗?
最近在玩本地 DNS 帆樯
hilyjiang
2017-05-19 10:46:48 +08:00
重点是,“ nodejs 的 runtime 默认使用了 4 个 worker 线程去处理 dns 解析请求”,为什么只有 4 个。
liaohongxing
2017-05-19 13:33:35 +08:00
一般无脑装了个 nscd 缓存 DNS,其他倒是没怎么关注
netssfy
2017-05-19 15:48:37 +08:00
@hilyjiang nodejs 用的线程池是由 libuv 库提供的,libuv 默认设置的就是 4,这个配置可修改.
通过环境变量 UV_THREADPOOL_SIZE 来修改,最大 128

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/362307

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX