Node.js dns.resolve() vs dns.lookup()

Justin picture Justin · Dec 6, 2016 · Viewed 19.1k times · Source

I need to lookup a given host to its corresponding IP in Node.js. There seems to be two native methods of doing this:

> dns.resolve('google.com', (error, addresses) => { console.error(error); console.log(addresses); });
QueryReqWrap {
  bindingName: 'queryA',
  callback: { [Function: asyncCallback] immediately: true },
  hostname: 'google.com',
  oncomplete: [Function: onresolve],
  domain:
   Domain {
     domain: null,
     _events: { error: [Function] },
     _eventsCount: 1,
     _maxListeners: undefined,
     members: [] } }
> null
[ '216.58.194.174' ]

And:

> dns.lookup('google.com', (error, address, family) => { console.error(error); console.log(address); console.log(family); });
GetAddrInfoReqWrap {
  callback: { [Function: asyncCallback] immediately: true },
  family: 0,
  hostname: 'google.com',
  oncomplete: [Function: onlookup],
  domain:
   Domain {
     domain: null,
     _events: { error: [Function] },
     _eventsCount: 1,
     _maxListeners: undefined,
     members: [] } }
> null
216.58.194.174
4

Both return the same IPv4 address. What is the difference between dns.lookup() and dns.resolve()? Also, which is more performant for lots of requests per second?

Answer

mscdex picture mscdex · Dec 6, 2016

The dns documentation already describes the difference:

Although dns.lookup() and the various dns.resolve*()/dns.reverse() functions have the same goal of associating a network name with a network address (or vice versa), their behavior is quite different. These differences can have subtle but significant consequences on the behavior of Node.js programs.

dns.lookup()
Under the hood, dns.lookup() uses the same operating system facilities as most other programs. For instance, dns.lookup() will almost always resolve a given name the same way as the ping command. On most POSIX-like operating systems, the behavior of the dns.lookup() function can be modified by changing settings in nsswitch.conf(5) and/or resolv.conf(5), but note that changing these files will change the behavior of all other programs running on the same operating system.

Though the call to dns.lookup() will be asynchronous from JavaScript's perspective, it is implemented as a synchronous call to getaddrinfo(3) that runs on libuv's threadpool. Because libuv's threadpool has a fixed size, it means that if for whatever reason the call to getaddrinfo(3) takes a long time, other operations that could run on libuv's threadpool (such as filesystem operations) will experience degraded performance. In order to mitigate this issue, one potential solution is to increase the size of libuv's threadpool by setting the 'UV_THREADPOOL_SIZE' environment variable to a value greater than 4 (its current default value). For more information on libuv's threadpool, see the official libuv documentation.

dns.resolve(), dns.resolve*() and dns.reverse()
These functions are implemented quite differently than dns.lookup(). They do not use getaddrinfo(3) and they always perform a DNS query on the network. This network communication is always done asynchronously, and does not use libuv's threadpool.

As a result, these functions cannot have the same negative impact on other processing that happens on libuv's threadpool that dns.lookup() can have.

They do not use the same set of configuration files than what dns.lookup() uses. For instance, they do not use the configuration from /etc/hosts.

As far as concurrency goes, you are better off using dns.resolve*() because those requests do not end up in the thread pool, whereas dns.lookup() requests do because they call out to the OS DNS resolver which typically blocks (although there are some kind of-async interfaces now -- but they are not necessarily implemented everywhere).

Currently, node internally uses dns.lookup() for any automatic DNS resolution, like when you pass a hostname to http.request().