Skip to content

Commit

Permalink
Resolve hostnames using netty's non-blocking DNS resolver #1498
Browse files Browse the repository at this point in the history
We now use netty's AddressResolverGroup and configure DnsResolverGroup if netty-dns-resolver is on the classpath.

Original pull request: #1517.
  • Loading branch information
yueki1993 authored and mp911de committed Dec 2, 2020
1 parent 12e4330 commit 122cfd3
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 6 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,12 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver-dns</artifactId>
<optional>true</optional>
</dependency>

<!-- OS-native transports -->

<dependency>
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/lettuce/core/AbstractRedisClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
* @author Mark Paluch
* @author Jongyeol Choi
* @author Poorva Gokhale
* @author Yohei Ueki
* @since 3.0
* @see ClientResources
*/
Expand Down Expand Up @@ -294,6 +295,15 @@ protected void channelType(ConnectionBuilder connectionBuilder, ConnectionPoint
}
}

protected void resolver(ConnectionBuilder connectionBuilder, ConnectionPoint connectionPoint) {

LettuceAssert.notNull(connectionPoint, "ConnectionPoint must not be null");

if (connectionPoint.getSocket() == null) {
connectionBuilder.bootstrap().resolver(clientResources.addressResolverGroup());
}
}

private EventLoopGroup getEventLoopGroup(Class<? extends EventLoopGroup> eventLoopGroupClass) {

for (;;) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/lettuce/core/RedisClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
*
* @author Will Glozer
* @author Mark Paluch
* @author Yohei Ueki
* @see RedisURI
* @see StatefulRedisConnection
* @see RedisFuture
Expand Down
26 changes: 24 additions & 2 deletions src/main/java/io/lettuce/core/Transports.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioChannelOption;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.internal.logging.InternalLogger;
Expand All @@ -40,9 +42,10 @@
* and domain socket transports.
*
* @author Mark Paluch
* @author Yohei Ueki
* @since 4.4
*/
class Transports {
public class Transports {

private static final InternalLogger logger = InternalLoggerFactory.getInstance(Transports.class);

Expand All @@ -61,7 +64,7 @@ static Class<? extends EventLoopGroup> eventLoopGroupClass() {
/**
* @return the default {@link Channel} for socket (network/TCP) transport.
*/
static Class<? extends Channel> socketChannelClass() {
public static Class<? extends Channel> socketChannelClass() {

if (NativeTransports.isAvailable()) {
return NativeTransports.socketChannelClass();
Expand All @@ -70,6 +73,18 @@ static Class<? extends Channel> socketChannelClass() {
return NioSocketChannel.class;
}

/**
* @return the default {@link DatagramChannel} for socket (network/UDP) transport.
*/
public static Class<? extends DatagramChannel> datagramChannelClass() {

if (NativeTransports.isSocketSupported()) {
return NativeTransports.datagramChannelClass();
}

return NioDatagramChannel.class;
}

/**
* Initialize the {@link Bootstrap} and apply {@link SocketOptions}.
*
Expand Down Expand Up @@ -146,6 +161,13 @@ static Class<? extends Channel> socketChannelClass() {
return RESOURCES.socketChannelClass();
}

/**
* @return the native transport socket {@link DatagramChannel} class.
*/
static Class<? extends DatagramChannel> datagramChannelClass() {
return RESOURCES.datagramChannelClass();
}

/**
* @return the native transport domain socket {@link Channel} class.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
* possible.
*
* @author Mark Paluch
* @author Yohei Ueki
* @since 3.0
* @see RedisURI
* @see StatefulRedisClusterConnection
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.lettuce.core.resource;

import java.util.function.Supplier;

import io.lettuce.core.Transports;
import io.netty.channel.socket.SocketChannel;
import io.netty.resolver.AddressResolverGroup;
import io.netty.resolver.DefaultAddressResolverGroup;
import io.netty.resolver.dns.DnsAddressResolverGroup;
import io.netty.resolver.dns.DnsNameResolverBuilder;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

/**
* 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
* {@literal netty-dns-resolver} becomes mandatory. Internal API.
*
* @author Yohei Ueki
* @since xxx
*/
class AddressResolverGroupProvider {

private static final InternalLogger logger = InternalLoggerFactory.getInstance(AddressResolverGroupProvider.class);

private static final AddressResolverGroup<?> ADDRESS_RESOLVER_GROUP;

static {
boolean dnsResolverAvailable;
try {
Class.forName("io.netty.resolver.dns.DnsAddressResolverGroup");
dnsResolverAvailable = true;
} catch (ClassNotFoundException e) {
dnsResolverAvailable = false;
}

// create addressResolverGroup instance via Supplier to avoid NoClassDefFoundError.
Supplier<AddressResolverGroup<?>> supplier;
if (dnsResolverAvailable) {
logger.debug("Starting with netty's non-blocking DNS resolver library");
supplier = AddressResolverGroupProvider::defaultDnsAddressResolverGroup;
} else {
logger.debug("Starting without optional netty's non-blocking DNS resolver library");
supplier = () -> DefaultAddressResolverGroup.INSTANCE;
}
ADDRESS_RESOLVER_GROUP = supplier.get();
}

/**
* Returns the {@link AddressResolverGroup} for dns resolution.
*
* @return the {@link DnsAddressResolverGroup} if {@literal netty-dns-resolver} is available, otherwise return
* {@link DefaultAddressResolverGroup#INSTANCE}.
* @since xxx
*/
static AddressResolverGroup<?> addressResolverGroup() {
return ADDRESS_RESOLVER_GROUP;
}

private static DnsAddressResolverGroup defaultDnsAddressResolverGroup() {
return new DnsAddressResolverGroup(new DnsNameResolverBuilder().channelType(Transports.datagramChannelClass())
.socketChannelType(Transports.socketChannelClass().asSubclass(SocketChannel.class)));
}

}
23 changes: 23 additions & 0 deletions src/main/java/io/lettuce/core/resource/ClientResources.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.lettuce.core.metrics.CommandLatencyCollectorOptions;
import io.lettuce.core.metrics.CommandLatencyRecorder;
import io.lettuce.core.tracing.Tracing;
import io.netty.resolver.AddressResolverGroup;
import io.netty.util.Timer;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
Expand All @@ -45,10 +46,12 @@
* <li>{@link DnsResolver} to collect latency details. Requires the {@literal LatencyUtils} library.</li>
* <li>{@link Timer} for scheduling</li>
* <li>{@link Tracing} to trace Redis commands.</li>
* <li>{@link AddressResolverGroup} for dns resolution.</li>
* </ul>
*
* @author Mark Paluch
* @author Mikhael Sokolov
* @author Yohei Ueki
* @since 3.4
* @see DefaultClientResources
*/
Expand Down Expand Up @@ -241,6 +244,18 @@ default Builder commandLatencyCollector(CommandLatencyCollector commandLatencyCo
*/
Builder tracing(Tracing tracing);

/**
* Sets the {@link AddressResolverGroup} for dns resolution. This option is only effective if
* {@link DnsResolvers#UNRESOLVED} is used as {@link DnsResolver}. Defaults to
* {@link io.netty.resolver.DefaultAddressResolverGroup#INSTANCE} if {@literal netty-dns-resolver} is not available,
* otherwise defaults to {@link io.netty.resolver.dns.DnsAddressResolverGroup}.
*
* @param addressResolverGroup the {@link AddressResolverGroup} instance, must not be {@code null}.
* @return {@code this} {@link Builder}
* @since xxx
*/
Builder addressResolverGroup(AddressResolverGroup<?> addressResolverGroup);

/**
* @return a new instance of {@link DefaultClientResources}.
*/
Expand Down Expand Up @@ -385,4 +400,12 @@ default Builder commandLatencyCollector(CommandLatencyCollector commandLatencyCo
*/
Tracing tracing();

/**
* Return the {@link AddressResolverGroup} instance for dns resolution.
*
* @return the address resolver group.
* @since xxx
*/
AddressResolverGroup<?> addressResolverGroup();

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import io.lettuce.core.metrics.MetricCollector;
import io.lettuce.core.resource.Delay.StatefulDelay;
import io.lettuce.core.tracing.Tracing;
import io.netty.resolver.AddressResolverGroup;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
Expand Down Expand Up @@ -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>
* </ul>
*
* @author Mark Paluch
* @author Yohei Ueki
* @since 3.4
*/
public class DefaultClientResources implements ClientResources {
Expand Down Expand Up @@ -103,6 +106,12 @@ public class DefaultClientResources implements ClientResources {
*/
public static final NettyCustomizer DEFAULT_NETTY_CUSTOMIZER = DefaultNettyCustomizer.INSTANCE;

/**
* Default {@link AddressResolverGroup}.
*/
public static final AddressResolverGroup<?> DEFAULT_ADDRESS_RESOLVER_GROUP = AddressResolverGroupProvider
.addressResolverGroup();

static {

int threads = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads",
Expand Down Expand Up @@ -147,6 +156,8 @@ public class DefaultClientResources implements ClientResources {

private final Tracing tracing;

private final AddressResolverGroup<?> addressResolverGroup;

private volatile boolean shutdownCalled = false;

protected DefaultClientResources(Builder builder) {
Expand Down Expand Up @@ -243,6 +254,7 @@ protected DefaultClientResources(Builder builder) {
reconnectDelay = builder.reconnectDelay;
nettyCustomizer = builder.nettyCustomizer;
tracing = builder.tracing;
addressResolverGroup = builder.addressResolverGroup;

if (!sharedTimer && timer instanceof HashedWheelTimer) {
((HashedWheelTimer) timer).start();
Expand Down Expand Up @@ -308,6 +320,8 @@ public static class Builder implements ClientResources.Builder {

private Tracing tracing = Tracing.disabled();

private AddressResolverGroup<?> addressResolverGroup = DEFAULT_ADDRESS_RESOLVER_GROUP;

private Builder() {
}

Expand Down Expand Up @@ -569,6 +583,25 @@ public Builder tracing(Tracing tracing) {
return this;
}

/**
* Sets the {@link AddressResolverGroup} for dns resolution. This option is only effective if
* {@link DnsResolvers#UNRESOLVED} is used as {@link DnsResolver}. Defaults to
* {@link io.netty.resolver.DefaultAddressResolverGroup#INSTANCE} if {@literal netty-dns-resolver} is not available,
* otherwise defaults to {@link io.netty.resolver.dns.DnsAddressResolverGroup}.
*
* @param addressResolverGroup the {@link AddressResolverGroup} instance, must not be {@code null}.
* @return {@code this} {@link ClientResources.Builder}
* @since xxx
*/
@Override
public Builder addressResolverGroup(AddressResolverGroup<?> addressResolverGroup) {

LettuceAssert.notNull(addressResolverGroup, "AddressResolverGroup must not be null");

this.addressResolverGroup = addressResolverGroup;
return this;
}

/**
* @return a new instance of {@link DefaultClientResources}.
*/
Expand Down Expand Up @@ -603,7 +636,7 @@ public DefaultClientResources.Builder mutate() {
.commandLatencyPublisherOptions(commandLatencyPublisherOptions()).dnsResolver(dnsResolver())
.eventBus(eventBus()).eventExecutorGroup(eventExecutorGroup()).reconnectDelay(reconnectDelay)
.socketAddressResolver(socketAddressResolver()).nettyCustomizer(nettyCustomizer()).timer(timer())
.tracing(tracing());
.tracing(tracing()).addressResolverGroup(addressResolverGroup());

builder.sharedCommandLatencyCollector = sharedEventLoopGroupProvider;
builder.sharedEventExecutor = sharedEventExecutor;
Expand Down Expand Up @@ -742,4 +775,9 @@ public Tracing tracing() {
return tracing;
}

@Override
public AddressResolverGroup<?> addressResolverGroup() {
return addressResolverGroup;
}

}
18 changes: 18 additions & 0 deletions src/main/java/io/lettuce/core/resource/EpollProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollDomainSocketChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.internal.SystemPropertyUtil;
Expand All @@ -39,6 +41,7 @@
* the {@literal netty-transport-native-epoll} library during runtime. Internal API.
*
* @author Mark Paluch
* @author Yohei Ueki
* @since 4.4
*/
public class EpollProvider {
Expand Down Expand Up @@ -168,6 +171,13 @@ public SocketAddress newSocketAddress(String socketPath) {
return null;
}

@Override
public Class<? extends DatagramChannel> datagramChannelClass() {

checkForEpollLibrary();
return null;
}

}

/**
Expand Down Expand Up @@ -209,6 +219,14 @@ public Class<? extends Channel> socketChannelClass() {
return EpollSocketChannel.class;
}

@Override
public Class<? extends DatagramChannel> datagramChannelClass() {

checkForEpollLibrary();

return EpollDatagramChannel.class;
}

@Override
public Class<? extends Channel> domainSocketChannelClass() {

Expand Down
Loading

0 comments on commit 122cfd3

Please sign in to comment.