diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/RoundRobinDnsAddressResolverGroup.java b/resolver-dns/src/main/java/io/netty/resolver/dns/RoundRobinDnsAddressResolverGroup.java index e3010dfdf1f..6ef8176dbd7 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/RoundRobinDnsAddressResolverGroup.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/RoundRobinDnsAddressResolverGroup.java @@ -21,7 +21,7 @@ import io.netty.channel.socket.DatagramChannel; import io.netty.resolver.AddressResolver; import io.netty.resolver.AddressResolverGroup; -import io.netty.resolver.RoundRobinInetSocketAddressResolver; +import io.netty.resolver.RoundRobinInetAddressResolver; import io.netty.resolver.NameResolver; import io.netty.util.internal.UnstableApi; @@ -48,10 +48,16 @@ public RoundRobinDnsAddressResolverGroup( super(channelFactory, nameServerAddresses); } + /** + * We need to override this method, not + * {@link #newNameResolver(EventLoop, ChannelFactory, DnsServerAddresses)}, + * because we need to eliminate possible caching of {@link io.netty.resolver.NameResolver#resolve} + * by {@link InflightNameResolver} created in {@link #newResolver(EventLoop, ChannelFactory, DnsServerAddresses)}. + */ @Override protected final AddressResolver newAddressResolver(EventLoop eventLoop, NameResolver resolver) throws Exception { - return new RoundRobinInetSocketAddressResolver(eventLoop, resolver); + return new RoundRobinInetAddressResolver(eventLoop, resolver).asAddressResolver(); } } diff --git a/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java b/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java index 4d9308e324a..7f5f5b220be 100644 --- a/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java +++ b/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java @@ -26,7 +26,7 @@ import java.util.List; /** - * A {@link AbstractAddressResolver} that resolves {@link InetAddress}. + * A {@link AbstractAddressResolver} that resolves {@link InetSocketAddress}. */ public class InetSocketAddressResolver extends AbstractAddressResolver { diff --git a/resolver/src/main/java/io/netty/resolver/RoundRobinInetAddressResolver.java b/resolver/src/main/java/io/netty/resolver/RoundRobinInetAddressResolver.java new file mode 100644 index 00000000000..a8188bf1dc0 --- /dev/null +++ b/resolver/src/main/java/io/netty/resolver/RoundRobinInetAddressResolver.java @@ -0,0 +1,80 @@ +/* + * Copyright 2016 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.resolver; + +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.Promise; +import io.netty.util.internal.ThreadLocalRandom; +import io.netty.util.internal.UnstableApi; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.List; + +/** + * A {@link NameResolver} that resolves {@link InetAddress} and force Round Robin by choosing a single address + * randomly in {@link #resolve(String)} and {@link #resolve(String, Promise)} + * if multiple are returned by the {@link NameResolver}. + * Use {@link #asAddressResolver()} to create a {@link InetSocketAddress} resolver + */ +@UnstableApi +public class RoundRobinInetAddressResolver extends InetNameResolver { + private final NameResolver nameResolver; + + /** + * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned by + * {@link #resolve(String)} + * @param nameResolver the {@link NameResolver} used for name resolution + */ + public RoundRobinInetAddressResolver(EventExecutor executor, NameResolver nameResolver) { + super(executor); + this.nameResolver = nameResolver; + } + + @Override + protected void doResolve(final String inetHost, final Promise promise) throws Exception { + // hijack the doResolve request, but do a doResolveAll request under the hood. + // Note that InetSocketAddress.getHostName() will never incur a reverse lookup here, + // because an unresolved address always has a host name. + resolveAll(inetHost).addListener(new FutureListener>() { + @Override + public void operationComplete(Future> future) throws Exception { + if (future.isSuccess()) { + List inetAddresses = future.getNow(); + int numAddresses = inetAddresses.size(); + if (numAddresses == 0) { + promise.setFailure(new UnknownHostException(inetHost)); + } else { + // if there are multiple addresses: we shall pick one at random + // this is to support the round robin distribution + promise.setSuccess(inetAddresses.get( + numAddresses == 1 ? 0 : ThreadLocalRandom.current().nextInt(numAddresses))); + } + } else { + promise.setFailure(future.cause()); + } + } + }); + } + + @Override + protected void doResolveAll(String inetHost, Promise> promise) throws Exception { + nameResolver.resolveAll(inetHost, promise); + } +} diff --git a/resolver/src/main/java/io/netty/resolver/RoundRobinInetSocketAddressResolver.java b/resolver/src/main/java/io/netty/resolver/RoundRobinInetSocketAddressResolver.java deleted file mode 100644 index 7b4785731ce..00000000000 --- a/resolver/src/main/java/io/netty/resolver/RoundRobinInetSocketAddressResolver.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2016 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.ThreadLocalRandom; -import io.netty.util.internal.UnstableApi; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.List; - -/** - * A {@link AbstractAddressResolver} that resolves {@link InetAddress} and chooses a single address randomly if multiple - * are returned by the {@link NameResolver}. - */ -@UnstableApi -public class RoundRobinInetSocketAddressResolver extends InetSocketAddressResolver { - - /** - * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned by - * {@link #resolve(java.net.SocketAddress)} - * @param nameResolver the {@link NameResolver} used for name resolution - */ - public RoundRobinInetSocketAddressResolver(EventExecutor executor, NameResolver nameResolver) { - super(executor, nameResolver); - } - - @Override - protected void doResolve(final InetSocketAddress unresolvedAddress, final Promise promise) - throws Exception { - // hijack the doResolve request, but do a doResolveAll request under the hood - // Note that InetSocketAddress.getHostName() will never incur a reverse lookup here, - // because an unresolved address always has a host name. - nameResolver.resolveAll(unresolvedAddress.getHostName()) - .addListener(new FutureListener>() { - @Override - public void operationComplete(Future> future) throws Exception { - if (future.isSuccess()) { - List inetAddresses = future.getNow(); - int numAddresses = inetAddresses.size(); - if (numAddresses == 0) { - promise.setFailure(new UnknownHostException(unresolvedAddress.getHostName())); - } else { - // if there are multiple addresses: we shall pick one at random - // this is to support the round robin distribution - int index = - (numAddresses == 1)? 0 : ThreadLocalRandom.current().nextInt(numAddresses); - promise.setSuccess(new InetSocketAddress(inetAddresses.get(index), - unresolvedAddress.getPort())); - } - } else { - promise.setFailure(future.cause()); - } - } - }); - } -}