这几天碰到一个小坑,花了点时间调研了下,希望对后人有帮助 描述: 设想你有一个应用,对外暴露 2 个接口 http://www.my.com/api/77http://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 挂了会发生什么?情况如下
- 在 66.66.66.66 这台代理 DNS 代理中,对 www.77.com,www.88.com的记录缓存都还没过期时一切正常
- 当 66 对 www.77.com的记录过期了,那么所有外部对你的 www.my.com/api/77的访问都将失败,因为你内部无法访问 www.77.com
- 坑的地方来了, 当 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