前言
我们可以使用Node.js的 http
模块进行网络请求,比如使用 http.get
方法进行__GET__请求。当时在高并发请求的情况下,很容易出现如下的错误:
Error: getaddrinfo ENOTFOUND www.baidu.com www.baidu.com:80
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:57:26)
或者是这样的错误:
Error: queryA ETIMEOUT www.baidu.com
at QueryReqWrap.onresolve [as oncomplete] (dns.js:197:19)
这两个错误都和 DNS
解析有关。
一种解决方案
在Google搜索一番就会找到如下的解决方案:
const opts = {
host: 'www.baidu.com',
family: 4
};
http.get(opts, (res) => {
// handle the response
})
关键点是在请求的时候添加了 family
这个参数,我们先来看一下这个参数的官方解释:
family <number> IP address family to use when resolving host and hostname. Valid values are 4 or 6. When unspecified, both IP v4 and v6 will be used.
大概的意思是:这个参数可以指定解析 host
和 hostname
的时候所使用的IP地址族。可接受的参数为 4
和 6
。如果不指定,会同时使用IP-v4和IP-v6。
所以,这种解决方案就是让Node在调用 dns
模块解析域名的时候,指定使用IP-v4。这样能够在一定程度上解决问题。为什么说们说是一定程度上呢?因为经过我的验证,当请求并发量继续增大的时,还是会存在问题。
一些猜想
Node使用 dns
模块来提供域名解析服务,当我们调用 http
、 net
等相关模块时,也会使用到 dns
模块来进行相关的操作。当然,dns
模块也可以单独使用,例如:
const dns = require('dns');
const len = 10000;
const run = () => {
for (let i = 0; i < len; i++) {
dns.resolve4('www.baidu.com', (err, addresses) => {
if (err) {
console.log(`i: ${i}`);
throw err;
process.exit(0);
}
console.log(addresses[0]);
});
}
};
run();
那为什么高并发请求的时候会出现问题呢?我的猜想是:dns
模块对域名解析有一定的性能限制,当并发量达到一定程度时,就会出现超时,从而导致各种问题。那为什么使用IP-v4就能得到一定程度的改善呢?我的猜想是:默认情况下,dns
模块使用的是IP-v4和IP-v6进行域名解析,在切换解析规则时或者使用不同的规则对性能有一定的依赖,当指定使用IP-v4的时候,能够使得 dns
模块发挥最佳的性能,从而使问题得到一定的改善。
一些建议
在一些高并发请求的场景(比如爬虫)下,很有可能会导致DNS无法正常解析的问题,比如使用 request
模块进行高并发请求的时候也会出现问题。除了上述提到的解决方案外,还应该合理控制好并发量。此外,也可以尝试使用高性能的DNS服务器来提供DNS解析的效率。
参考资料
@XadillaX 死月大佬曾经修复过 dns
模块相关的bug,希望能从更深层次帮忙解释一下这个问题。谢谢~~
我印象中,linux服务器默认是不缓存dns解析结果的,每次http请求发出去都会请求dns服务器,所以其实也有不少dns cache方面的库。
https://www.npmjs.com/package/cache-dns
你看看能不能指定http请求使用自定义的dns实例,如果可以的话,就换成这种dns cache的库。
@alsotang 感谢大佬提供思路。我晚上回去试试。
还有65535端口的问题,服务端没有这个限制,但客户端有,一个客户端请求就会占用一个端口,准确的说是一个IP一个连接占用一个端口
linux 系统装个 dnsmasq 解决。
@waitingsong dnsmasq的方案太重了吧
今天又遇到这个问题,然后 Google 搜到了这个帖子,没想到有朝一日挖自己的坟贴。。。
这方法还挺管用,8k多次的请求用此方法不再报错。