New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use netty nio DNS resolver by default in HttpClient #1581
Conversation
Bootstrap bootstrap = new Bootstrap() | ||
.remoteAddress(key.host, key.port) | ||
.group(key.execution.getEventLoop()) | ||
.resolver(key.execution.maybeGet(AddressResolverGroup.class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the same pattern we do over in raptack-retrofit2
, but it's ugly and still requires that users explicitly bind or provide an ExecInitializer
that registers AddressResolverGroup
to forked executions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this pattern, so that we looking in the execution and if nothing is there, we get the default netty backed resolver (which defaults its configuration to the system properties in /etc/resolv.conf
). This means a user will still need to explicitly ensure that AddressResolverGroup
is registered in any forked executions (including ParallelBatch
) if they do any customizations.
Also, not sure what the impact of creating/gc'ing those would be....it doesn't look like there's any state really.
One oddity here, is that we are finding the AddressResolverGroup
from the Execution
, if you are using Guice and injecting HttpClient
, the AddressResolverGroup
doesn't come along with it. It's almost as though HttpClient
should also inject AddressResolverGroup
, but the Ratpack Registry
doesn't do dependency binding.
DnsNameResolverBuilder resolverBuilder = new DnsNameResolverBuilder() | ||
.eventLoop(execController.getEventLoopGroup().next()) | ||
.channelType(TransportDetector.getDatagramChannelImpl()) | ||
.socketChannelType(TransportDetector.getSocketChannelImpl()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is essentially the bare minimum for the resolver.
Should we provide some type of configuration via ServerConfig
to provide tunings here or just ask the user to provide their own implementation of AddressResolverGroup
on the Registry?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be very beneficial if we can configure the dns query timeout at the least as default.
But, there is already a way to provide few configurations with this change: netty/netty@4f72cdf#diff-e7ae8540514a0e3caa415abc27aaee9fe93b7732d318d05a9534686447a162b9L146 .
Other tuning configs would become tricky to expose here just because they are internal to netty and are in plenty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't see that previously @amityatagiri, good find.
It does look like the query time is configured based on the the timeout:
option in /etc/resolv.conf
(https://github.com/netty/netty/blob/4.1/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java#L334).
So if this is configurable here, what about the benefit of providing a config option inside of Ratpack?
I'd probably lean toward not providing any options here since they are configurable and obtained from the OS by default and ask the user to supply a fully configured AddressResolverGroup
if they want to tweak things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1.9.x branch is still pointing to netty 4.1.48.Final
. We may want to match it with the master branch or to the latest netty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes,
master
is the 2.x branch right now, that's why it's updated. But we can do that updated in 1.9.x
as well. @amityatagiri can you open another PR that is targeted to 1.9.x
with that dependency change?
@@ -46,6 +48,7 @@ public ReceivedResponse request(HttpClient httpClient, URI uri, ExecController e | |||
AtomicReference<ExecResult<ReceivedResponse>> result = new AtomicReference<>(); | |||
|
|||
execController.fork() | |||
.register(r -> r.add(AddressResolverGroup.class, DefaultAddressResolverGroup.INSTANCE)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does the necessary registry add so that the HttpClient
works, but could be removed if we a more holistic approach to this.
@@ -116,6 +125,7 @@ public static Registry buildBaseRegistry(RatpackServer ratpackServer, Imposition | |||
.add(Clock.class, Clock.systemDefaultZone()) | |||
.add(RatpackServer.class, ratpackServer) | |||
.add(ObjectMapper.class, new ObjectMapper()) | |||
.add(AddressResolverGroup.class, new DnsAddressResolverGroup(resolverBuilder)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Users can switch back to the default JVM implementation by doing:
.add(AddressResolverGroup.class, DefaultAddressResolverGroup.INSTANCE)
in the user registry.
|
||
public static AddressResolverGroup<?> defaultAddressResolver(ExecController controller) { | ||
DnsNameResolverBuilder resolverBuilder = new DnsNameResolverBuilder() | ||
.eventLoop(controller.getEventLoopGroup().next()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does mean that all DNS resolves are bound to 1 event loop...which, since it's NIO shouldn't be a problem (and it is how reactor-netty
does it too).
9a68f92
to
f237f69
Compare
Allows for overriding the implementation by adding AddressResolverGroup to the Registry.
f237f69
to
249b9ea
Compare
@@ -88,6 +95,10 @@ private DefaultHttpClient(DefaultHttpClient.Spec spec) { | |||
} else { | |||
proxy = null; | |||
} | |||
if (spec.resolver == null) { | |||
LOGGER.warn("DNS resolver not specified for HttpClientSpec, defaulting to JVM blocking implementation"); | |||
spec.resolver = HttpClient.blockingResolver(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do the only sane thing we can if someone is creating a new HttpClient and doesn't provide a resolver, which is use the blocking implementation but WARN about it.
We can't default to a non-blocking implementation because we don't have a handle to an EventLoop
which is needed. We could defer to to the point that we make the request but that would create high GC churn and possible performance impacts as each request would create its own resolver.
* @since 1.9.0 | ||
*/ | ||
static AddressResolverGroup<?> addressResolver(ExecController controller, Action<? super DnsNameResolverBuilder> spec) throws Exception { | ||
DnsNameResolverBuilder resolverBuilder = new DnsNameResolverBuilder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The builder automatically loads defaults from /etc/resolv.conf
.
@@ -256,6 +256,7 @@ private HttpClient httpClient() { | |||
.byteBufAllocator(TestByteBufAllocators.LEAKING_UNPOOLED_HEAP) | |||
.maxContentLength(Integer.MAX_VALUE) | |||
.poolSize(8) | |||
.addressResolver(HttpClient.blockingResolver()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is the blocking client for tests, use the blocking resolver and avoid the warning.
e2d0032
to
9cde3bf
Compare
Provide necessary defaults and helper methods for configuring.
9cde3bf
to
28a1ab6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@johnrengelman pls check my changes. I haven't observed the full CI response yet, but will do so in the next few days. |
@ldaley Looks good. I pushed fixes for the broken tests. Biggest piece of that was some HttpClient metrics classes that were not elevated to the public API so we were casting to |
This reverts commit 33376f7.
@johnrengelman I reverted the move to make the stats public. It wasn't necessary, and should be done as its own change if that's what we want to do. On that, I'm not sure it is what we want to do. As is stands, we are putting the core client on the hook for maintaining too much information by having it be responsible for providing “stats”. What we should do here instead is allow “event listeners” to be attached that can be notified of various things happening, and they can interpret that into whatever kind of stats they want and with what retention they want. |
Yeah. That’s a better plan. Something we should address in trunk. This looks good to me. |
Allows for overriding the implementation by adding AddressResolverGroup
to the Registry.
This change is