diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolverGroup.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolverGroup.java index 27f7d2ab01..9e9eceee97 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolverGroup.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolverGroup.java @@ -87,7 +87,7 @@ public class DnsAddressResolverGroup extends AddressResolverGroup newAddressResolver(EventLoop eventLoop, + NameResolver resolver) + throws Exception { + return new InetSocketAddressResolver(eventLoop, resolver); + } } 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 new file mode 100644 index 0000000000..e3010dfdf1 --- /dev/null +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/RoundRobinDnsAddressResolverGroup.java @@ -0,0 +1,57 @@ +/* + * 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.dns; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.EventLoop; +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.NameResolver; +import io.netty.util.internal.UnstableApi; + +import java.net.InetAddress; +import java.net.InetSocketAddress; + +/** + * A {@link AddressResolverGroup} of {@link DnsNameResolver}s that supports random selection of destination addresses if + * multiple are provided by the nameserver. This is ideal for use in applications that use a pool of connections, for + * which connecting to a single resolved address would be inefficient. + */ +@UnstableApi +public class RoundRobinDnsAddressResolverGroup extends DnsAddressResolverGroup { + + public RoundRobinDnsAddressResolverGroup( + Class channelType, + DnsServerAddresses nameServerAddresses) { + super(channelType, nameServerAddresses); + } + + public RoundRobinDnsAddressResolverGroup( + ChannelFactory channelFactory, + DnsServerAddresses nameServerAddresses) { + super(channelFactory, nameServerAddresses); + } + + @Override + protected final AddressResolver newAddressResolver(EventLoop eventLoop, + NameResolver resolver) + throws Exception { + return new RoundRobinInetSocketAddressResolver(eventLoop, resolver); + } +} diff --git a/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java b/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java index a710914385..4d9308e324 100644 --- a/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java +++ b/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java @@ -30,7 +30,7 @@ import java.util.List; */ public class InetSocketAddressResolver extends AbstractAddressResolver { - private final NameResolver nameResolver; + final NameResolver nameResolver; /** * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned diff --git a/resolver/src/main/java/io/netty/resolver/RoundRobinInetSocketAddressResolver.java b/resolver/src/main/java/io/netty/resolver/RoundRobinInetSocketAddressResolver.java new file mode 100644 index 0000000000..7b4785731c --- /dev/null +++ b/resolver/src/main/java/io/netty/resolver/RoundRobinInetSocketAddressResolver.java @@ -0,0 +1,75 @@ +/* + * 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()); + } + } + }); + } +}