As you all know Mojolicious uses non-blocking IO for network operations. This allows
Mojo::Server::* to process many requests simultaneously, make many http requests with
Mojo::UserAgent in parallel and other cool things.
But there is one hickey which may block whole non-blocking app with Mojolicious. It is known as host name resolution. For now Mojolicious uses blocking host name resolution provided by
IO::Socket::* backend. This is
IO::Socket::IP. This is not the problem if you write web application which will just handle requests and will not produce any requests to internet itself. But if your application actively uses
Mojo::IOLoop::Client, for example, and your DNS server is not so fast, whole application may suffer and even misbehave. You may think that this is not the problem because your DNS server is fast enough: it resolves
google.com to ip in 0.001 sec. But in fact your DNS server may return result for one domain in 0.001 sec and in 30 sec for other. No any warranty here. And all 30 sec your application will be blocked.
But why till now no one has written non-blocking DNS resolver for Mojolicious? In fact Mojolicious had non-blocking resolver since v0.999931 till v2.27. But as sri said this resolver sucked. This is why it was removed. And the only available alternative, as sri said, is to use
getaddrinfo(3) from separate thread. This is how i started Net::DNS::Native few weeks ago.
I think it will be cool to make pluggable API for DNS resolver, something similar to
Mojo::Reactor. The idea is to make
Mojo::Resolver base class with only one usefull method
detect() which will detect and load proper subclass. And two virtual methods
cancel() which should be overloaded in subclass:
- $id = resolve($host, $record_types, $port, $cb($result, $error))
$hostto ip address(es) in convenient form. For IPv6 (IO::Socket::IP) this should be result in form of
Socket::getaddrinfo(), so we can use
IO::Socket::IP. For IPv4 (IO::Socket::INET) this should be one ip in
xxx.xxx.xxx.xxxform, so we can use
$record_typesis array ref with possible values 'A' or 'AAAA' or both.
$portis port number to pass to
getaddrinfo(3)(optional, if only 'A' record type specified).
$cbis callback which will be called on finish.
Two parameters will be passed to callback.
$resultis undef on error, 4 dotted ip as string if only 'A' record type specified, array ref with same values as
Socket::getaddrinfo()would return if
$record_typescontains 'AAAA' record type. If
$errorshould contain error message.
resolve()should return unique id associated with this operation.
cancel()should stop resolving operation associated with
$id. This is usefull for timeouts.
Now we have base class and should implement some subclasses. Like
Mojo::Reactor::EV we need to implement one subclass which will be default and one more powerfull which may require additional module.
Not sure about names, but let's call default resolver as
Mojo::Resolver::Default. And this will be just simple class which will call blocking
Socket::getaddrinfo() depending on record type. So in fact this will be same behavior as now.
And other subclass should be really non-blocking. Let's call it
Mojo::Resolver::Native with dependency from
Then we'll add
resolver attribute to
Mojo::IOLoop and correct a little
Mojo::IOLoop::Client::connect() to use this resolver.
So, now we have optional non-blocking resolver in the core and pluggable API, so anyone can write cpan module like
Mojo::Resolver::NetDNS or other.