Skip to content
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

Merged
merged 10 commits into from Feb 22, 2021

Conversation

johnrengelman
Copy link
Member

@johnrengelman johnrengelman commented Feb 10, 2021

Allows for overriding the implementation by adding AddressResolverGroup
to the Registry.


This change is Reviewable

Bootstrap bootstrap = new Bootstrap()
.remoteAddress(key.host, key.port)
.group(key.execution.getEventLoop())
.resolver(key.execution.maybeGet(AddressResolverGroup.class)
Copy link
Member Author

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.

Copy link
Member Author

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());
Copy link
Member Author

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?

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Member Author

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))
Copy link
Member Author

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))
Copy link
Member Author

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.

@johnrengelman johnrengelman added this to the release-1.9.0 milestone Feb 11, 2021

public static AddressResolverGroup<?> defaultAddressResolver(ExecController controller) {
DnsNameResolverBuilder resolverBuilder = new DnsNameResolverBuilder()
.eventLoop(controller.getEventLoopGroup().next())
Copy link
Member Author

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).

@johnrengelman johnrengelman force-pushed the feature/configurable-dns-resolver branch from 9a68f92 to f237f69 Compare February 11, 2021 15:14
John Engelman added 2 commits February 14, 2021 18:14
Allows for overriding the implementation by adding AddressResolverGroup
to the Registry.
@johnrengelman johnrengelman force-pushed the feature/configurable-dns-resolver branch from f237f69 to 249b9ea Compare February 15, 2021 01:01
@@ -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();
Copy link
Member Author

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()
Copy link
Member Author

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())
Copy link
Member Author

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.

@johnrengelman johnrengelman force-pushed the feature/configurable-dns-resolver branch 2 times, most recently from e2d0032 to 9cde3bf Compare February 15, 2021 01:41
Provide necessary defaults and helper methods for configuring.
@johnrengelman johnrengelman force-pushed the feature/configurable-dns-resolver branch from 9cde3bf to 28a1ab6 Compare February 15, 2021 04:17
Copy link
Contributor

@amityatagiri amityatagiri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ldaley
Copy link
Member

ldaley commented Feb 20, 2021

@johnrengelman pls check my changes. I haven't observed the full CI response yet, but will do so in the next few days.

@johnrengelman
Copy link
Member Author

@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 DefaultHttpClient to get them. I've moved those to the public API which requires any HttpClient to now implement them.

@ldaley
Copy link
Member

ldaley commented Feb 21, 2021

@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.

@johnrengelman
Copy link
Member Author

Yeah. That’s a better plan. Something we should address in trunk.

This looks good to me.

@ldaley ldaley changed the title Use netty nio DNS resolver by default in HttpClient. Use netty nio DNS resolver by default in HttpClient Feb 22, 2021
@ldaley ldaley merged commit e104606 into 1.9.x Feb 22, 2021
@ldaley ldaley deleted the feature/configurable-dns-resolver branch February 22, 2021 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants