-
Notifications
You must be signed in to change notification settings - Fork 996
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
Resolve hostnames using netty's non-blocking DNS resolver #1517
Resolve hostnames using netty's non-blocking DNS resolver #1517
Conversation
2186106
to
67b9243
Compare
We now use netty's non-blocking DNS resolver upon connect.
67b9243
to
bb645d2
Compare
connectionBuilder.bootstrap().resolver( | ||
new DnsAddressResolverGroup(new DnsNameResolverBuilder().channelType(Transports.datagramChannelClass()) | ||
.socketChannelType(Transports.socketChannelClass().asSubclass(SocketChannel.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.
[Supplemental Comments]
The key point of this PR. cf: https://netty.io/news/2016/05/26/4-1-0-Final.html
If we want to use non-blocking dns resolver, we need to pass DnsAddressResolverGroup
to Bootstrap
.
Also note that netty encourages users to support TCP fallback in Netty 4.1.37 release note:
https://netty.io/news/2019/06/28/4-1-37-Final.html
I followed Netty 4.1.37 release note, i.e., use DnsNameResolverBuilder
with DatagramChannel
(UDP) and SocketChannel
(TCP).
Since Transports.socketChannelClass()
returns Class<? extends Channel>
and DnsNameResolverBuilder#socketChannelType
wants Class<? extends SocketChannel> channelType
,
we need to cast the return value using asSubclass
(or change the return type of Transports#socketChannelClass
).
I didn't attach a test code about this but for just in case I checked we can cast known socket channels safely like:
@Test
void socketChannelClassCastTest() {
// if failed ClassCastException is thrown.
NioSocketChannel.class.asSubclass(SocketChannel.class);
KQueueSocketChannel.class.asSubclass(SocketChannel.class);
EpollSocketChannel.class.asSubclass(SocketChannel.class);
}
channelType(connectionBuilder, redisURI); | ||
resolver(connectionBuilder, redisURI); |
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.
[Supplemental Comments]
I added resolver(...)
all places where channelType(...)
is used.
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 think it makes sense to refactor the connect code a bit to pull things more together. I'll take care of this during the merge.
[Question] |
/** | ||
* @return the {@link DatagramChannel} class. | ||
*/ | ||
Class<? extends DatagramChannel> datagramChannelClass(); |
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.
[Question; nits]
For consistency with socketChannelClass()
, is it better to declare here as Class<? extends Channel> datagramChannelClass()
?
It seems a bit wired because a method for TCP returns Channel
while a method for UDP returns DatagramChannel
.
Note that if we return Class<? extends Channel>
here, we need cast the return value at
https://github.com/lettuce-io/lettuce-core/pull/1517/files#diff-954f0da1e02d2af777f9afc758b8b20f3f2df40a221f1a0e3569ef05402ccd97R310
Codecov Report
@@ Coverage Diff @@
## main #1517 +/- ##
=========================================
Coverage 78.86% 78.86%
- Complexity 6290 6302 +12
=========================================
Files 470 471 +1
Lines 21022 21071 +49
Branches 2315 2317 +2
=========================================
+ Hits 16578 16618 +40
- Misses 3378 3386 +8
- Partials 1066 1067 +1
Continue to review full report at Codecov.
|
Thanks a lot for your pull request. The changes look pretty decent. Don't worry about since tag, we can add those during the merge. All changes touch internal API and not so much user-facing API. I'm wondering whether we should introduce some means of caching ( Going further, we should probably deprecate the DNS support classes we provide by Lettuce as we plan to use Netty's DNS resolution mechanism. I'd suggest making Netty's DNS resolution optional, that is, use it when |
Thanks for your review! To Comment1:
DnsCacheOverviewI examined netty's implementation in detail. If those objects are not passed to Basically they follow a TTL returned by a DNS server. It seems netty does not respect DnsCachesummaryBy default the default behaviour is like:
detailsBy default As you see the source code, the behaviour changes whether If additionals is not provided:For successful dns queries, those will be cached here with a TTL returned by DNS server. For failed dns queries, those will be cached here. However, by default Do we need to care a case of additional is provided?Maybe no. At least I confirmed by debugger this method is used when connect so DefaultDnsCnameCachesummaryBy default this class also follows a TTL returned by a DNS server. details
AuthoritativeDnsServerCachesummaryBy default this class also follows a TTL returned by a DNS server. To Comment2:
I strongly agree to make I will add like:
(Maybe I need to change To Comment3:
I will answer to this tomorrow or later: To Comment4:
I will answer to this tomorrow or later: |
Regarding the configuration So in general, we would be able to configure That also simplifies the cache since a |
Maybe I could catch up what you want to say regarding DNS cache; unless netty's cache objects are reused, those are newly created every time lettuce creates a new connection, which means DNS caches do not work as we intended. I think we have following remaining tasks:
It seems better to separate pull requests (a pull request of task1&2, and a pull request for task3).
|
Making |
Thanks! I will tackle task1&2 at the weekend. |
01287aa
to
ec85577
Compare
@@ -34,7 +34,7 @@ | |||
* @author Yohei Ueki | |||
* @since 4.4 | |||
*/ | |||
class Transports { | |||
public class Transports { |
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.
[Supplemental Comments]
I made this public because this is now referenced by outside of the package.
ec85577#diff-724688b7234bd44bd8e5f08ce8b0a616ecaf11729ebd99ad72d510431878a6f4R60-R63
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.
We should move Transports
to the resource
package, that's something we can do during the merge.
|
||
private static final AddressResolverGroup<?> ADDRESS_RESOLVER_GROUP; | ||
|
||
static { |
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.
These should be unit-tested, but I could not come up with a good way to write unit tests regarding this in lettuce's test repository, because we need unittest jars with and without netty-dns-resolver. We might be able do it by dirty-hacking class loader, but I am not quite familiar with it.
I wrote a unit test code outside of this repo:
https://github.com/yueki1993/lettuce-test-PR1517/blob/main/lettuce-test-PR1517-without-netty-dns-resolver/src/test/java/com/github/yueki1993/lettuce_test_PR1517/without_netty_dns_resolver/AddressResolverGroupTests.java
https://github.com/yueki1993/lettuce-test-PR1517/blob/main/lettuce-test-PR1517-with-netty-dns-resolver/src/test/java/com/github/yueki1993/lettuce_test_PR1517/with_netty_dns_resolver/AddressResolverGroupTests.java
[Question] /**
* Sets the {@link DnsResolver} that is used to resolve hostnames to {@link java.net.InetAddress}. Defaults to
* {@link DnsResolvers#JVM_DEFAULT}
*
* @param dnsResolver the DNS resolver, must not be {@code null}.
* @return {@code this} {@link Builder}.
* @since 4.3
*/
Builder dnsResolver(DnsResolver dnsResolver); I think DnsResolver currently defaults to |
dnsResolverAvailable = false; | ||
} | ||
|
||
// create addressResolverGroup instance via Supplier to avoid NoClassDefFoundError. |
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.
[Supplemental Comments]
Actually I am not so familiar with jvm's classloader.
I found developer docs of jenkins, and they say using generics types will not raise NoClassDefFoundError.
Thus I wrapped this by Supplier<AddressResolverGroup<?>>
and created AddressResolverGroup<?>
instance via supplier.
https://www.jenkins.io/doc/developer/plugin-development/optional-dependencies/
|
||
/** | ||
* Wraps and provides {@link AddressResolverGroup} classes. This is to protect the user from {@link ClassNotFoundException}'s | ||
* caused by the absence of the {@literal netty-dns-resolver} library during runtime. This class will be deleted when |
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.
[Supplemental Comments]
This class can be deleted when netty-dns-resolver
becomes mandatory: simply replace here with
https://github.com/lettuce-io/lettuce-core/pull/1517/files#diff-596af0e5ee58e7de4607c1c26b0ab4bfad308c0f2a713d52caa5a68a2aa1cf9cR112-R113
public static final AddressResolverGroup<?> DEFAULT_ADDRESS_RESOLVER_GROUP = new DnsAddressResolverGroup(new DnsNameResolverBuilder().channelType(Transports.datagramChannelClass())
.socketChannelType(Transports.socketChannelClass().asSubclass(SocketChannel.class)));
Hi @mp911de,
Could you review my commits?
Raised an issue: #1527 |
d0bbf2c
to
13cd028
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.
Looks good. I left a few minor comments. We can sort out the remaining issues during the merge. Care to squash your commits?
channelType(connectionBuilder, redisURI); | ||
resolver(connectionBuilder, redisURI); |
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 think it makes sense to refactor the connect code a bit to pull things more together. I'll take care of this during the merge.
@@ -34,7 +34,7 @@ | |||
* @author Yohei Ueki | |||
* @since 4.4 | |||
*/ | |||
class Transports { | |||
public class Transports { |
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.
We should move Transports
to the resource
package, that's something we can do during the merge.
static { | ||
boolean dnsResolverAvailable; | ||
try { | ||
Class.forName("io.netty.resolver.dns.DnsAddressResolverGroup"); |
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.
You can use LettuceClassUtils.isPresent(…)
.
return ADDRESS_RESOLVER_GROUP; | ||
} | ||
|
||
private static DnsAddressResolverGroup defaultDnsAddressResolverGroup() { |
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 method signature creates a dependency to DnsAddressResolverGroup
that the class loader tries to resolve when the class gets loaded. Please inline this method.
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.
As far as I tried with my UT and jenkins' doc, this did not produce ClassNotFoundError
or something, because it is declared as private and it is not upcasted.
But i don't have strong reason to stick with this, so this also leave to you.
* | ||
* @param addressResolverGroup the {@link AddressResolverGroup} instance, must not be {@code null}. | ||
* @return {@code this} {@link Builder} | ||
* @since xxx |
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 6.1
@@ -70,9 +71,11 @@ | |||
* <li>a {@code socketAddressResolver} which is a provided instance of {@link SocketAddressResolver}.</li> | |||
* <li>a {@code timer} that is a provided instance of {@link io.netty.util.HashedWheelTimer}.</li> | |||
* <li>a {@code tracing} that is a provided instance of {@link Tracing}.</li> | |||
* <li>a {@code addressResolverGroup} that is a provided instance of {@link AddressResolverGroup}.</li> |
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.
Nit: We try to sort elements alphabetically.
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.
(nits) also i noticed it should be an {@code addressResolverGroup} ...
* | ||
* @param addressResolverGroup the {@link AddressResolverGroup} instance, must not be {@code null}. | ||
* @return {@code this} {@link ClientResources.Builder} | ||
* @since xxx |
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 6.1
Thanks, I leave remaining issues to you if ok (because it seems quicker than reviewing again). |
We now use netty's AddressResolverGroup and configure DnsResolverGroup if netty-dns-resolver is on the classpath. Original pull request: #1517.
Thank you for your contribution. That's merged, squashed, and polished now. |
Closes: #1498
We now use netty's non-blocking DNS resolver upon connect if optional dependency
netty-dns-resolver
is provided.Make sure that:
I confirmed netty's non-blocking DNS resolver is actually used by debugger.
(Debug run
AtLeastOnceTest#connectionIsConnectedAfterConnect
with a breakpoint athttps://github.com/netty/netty/blob/netty-4.1.53.Final/transport/src/main/java/io/netty/bootstrap/Bootstrap.java#L206)