Skip to content

Commit ec85577

Browse files
committed
make netty-resolver-dns optional
1 parent bb645d2 commit ec85577

File tree

7 files changed

+146
-18
lines changed

7 files changed

+146
-18
lines changed

pom.xml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,6 @@
154154
<artifactId>netty-handler</artifactId>
155155
</dependency>
156156

157-
<dependency>
158-
<groupId>io.netty</groupId>
159-
<artifactId>netty-resolver-dns</artifactId>
160-
</dependency>
161-
162157
<dependency>
163158
<groupId>io.netty</groupId>
164159
<artifactId>netty-transport</artifactId>
@@ -210,6 +205,12 @@
210205
<optional>true</optional>
211206
</dependency>
212207

208+
<dependency>
209+
<groupId>io.netty</groupId>
210+
<artifactId>netty-resolver-dns</artifactId>
211+
<optional>true</optional>
212+
</dependency>
213+
213214
<!-- OS-native transports -->
214215

215216
<dependency>

src/main/java/io/lettuce/core/AbstractRedisClient.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@
5454
import io.netty.channel.group.ChannelGroup;
5555
import io.netty.channel.group.DefaultChannelGroup;
5656
import io.netty.channel.nio.NioEventLoopGroup;
57-
import io.netty.channel.socket.SocketChannel;
58-
import io.netty.resolver.dns.DnsAddressResolverGroup;
59-
import io.netty.resolver.dns.DnsNameResolverBuilder;
6057
import io.netty.util.concurrent.EventExecutorGroup;
6158
import io.netty.util.concurrent.Future;
6259
import io.netty.util.internal.logging.InternalLogger;
@@ -306,9 +303,7 @@ protected void resolver(ConnectionBuilder connectionBuilder, ConnectionPoint con
306303
LettuceAssert.notNull(connectionPoint, "ConnectionPoint must not be null");
307304

308305
if (connectionPoint.getSocket() == null) {
309-
connectionBuilder.bootstrap().resolver(
310-
new DnsAddressResolverGroup(new DnsNameResolverBuilder().channelType(Transports.datagramChannelClass())
311-
.socketChannelType(Transports.socketChannelClass().asSubclass(SocketChannel.class))));
306+
connectionBuilder.bootstrap().resolver(clientResources.addressResolverGroup());
312307
}
313308
}
314309

src/main/java/io/lettuce/core/Transports.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
* @author Yohei Ueki
3535
* @since 4.4
3636
*/
37-
class Transports {
37+
public class Transports {
3838

3939
/**
4040
* @return the default {@link EventLoopGroup} for socket transport that is compatible with {@link #socketChannelClass()}.
@@ -51,7 +51,7 @@ static Class<? extends EventLoopGroup> eventLoopGroupClass() {
5151
/**
5252
* @return the default {@link Channel} for socket (network/TCP) transport.
5353
*/
54-
static Class<? extends Channel> socketChannelClass() {
54+
public static Class<? extends Channel> socketChannelClass() {
5555

5656
if (NativeTransports.isSocketSupported()) {
5757
return NativeTransports.socketChannelClass();
@@ -63,7 +63,7 @@ static Class<? extends Channel> socketChannelClass() {
6363
/**
6464
* @return the default {@link DatagramChannel} for socket (network/UDP) transport.
6565
*/
66-
static Class<? extends DatagramChannel> datagramChannelClass() {
66+
public static Class<? extends DatagramChannel> datagramChannelClass() {
6767

6868
if (NativeTransports.isSocketSupported()) {
6969
return NativeTransports.datagramChannelClass();
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.lettuce.core.resource;
2+
3+
import java.util.function.Supplier;
4+
5+
import io.lettuce.core.Transports;
6+
import io.netty.channel.socket.SocketChannel;
7+
import io.netty.resolver.AddressResolverGroup;
8+
import io.netty.resolver.DefaultAddressResolverGroup;
9+
import io.netty.resolver.dns.DnsAddressResolverGroup;
10+
import io.netty.resolver.dns.DnsNameResolverBuilder;
11+
import io.netty.util.internal.logging.InternalLogger;
12+
import io.netty.util.internal.logging.InternalLoggerFactory;
13+
14+
/**
15+
* Wraps and provides {@link AddressResolverGroup} classes. This is to protect the user from {@link ClassNotFoundException}'s
16+
* caused by the absence of the {@literal netty-dns-resolver} library during runtime. This class will be deleted when
17+
* {@literal netty-dns-resolver} becomes mandatory. Internal API.
18+
*
19+
* @author Yohei Ueki
20+
* @since xxx
21+
*/
22+
class AddressResolverGroupProvider {
23+
24+
private static final InternalLogger logger = InternalLoggerFactory.getInstance(AddressResolverGroupProvider.class);
25+
26+
private static final AddressResolverGroup<?> ADDRESS_RESOLVER_GROUP;
27+
28+
static {
29+
boolean dnsResolverAvailable;
30+
try {
31+
Class.forName("io.netty.resolver.dns.DnsAddressResolverGroup");
32+
dnsResolverAvailable = true;
33+
} catch (ClassNotFoundException e) {
34+
dnsResolverAvailable = false;
35+
}
36+
37+
// create addressResolverGroup instance via Supplier to avoid NoClassDefFoundError.
38+
Supplier<AddressResolverGroup<?>> supplier;
39+
if (dnsResolverAvailable) {
40+
logger.debug("Starting with netty's non-blocking DNS resolver library");
41+
supplier = AddressResolverGroupProvider::defaultDnsAddressResolverGroup;
42+
} else {
43+
logger.debug("Starting without optional netty's non-blocking DNS resolver library");
44+
supplier = () -> DefaultAddressResolverGroup.INSTANCE;
45+
}
46+
ADDRESS_RESOLVER_GROUP = supplier.get();
47+
}
48+
49+
/**
50+
* Returns the {@link AddressResolverGroup} for dns resolution.
51+
*
52+
* @return the {@link DnsAddressResolverGroup} if {@literal netty-dns-resolver} is available, otherwise return
53+
* {@link DefaultAddressResolverGroup#INSTANCE}.
54+
* @since xxx
55+
*/
56+
static AddressResolverGroup<?> addressResolverGroup() {
57+
return ADDRESS_RESOLVER_GROUP;
58+
}
59+
60+
private static DnsAddressResolverGroup defaultDnsAddressResolverGroup() {
61+
return new DnsAddressResolverGroup(new DnsNameResolverBuilder().channelType(Transports.datagramChannelClass())
62+
.socketChannelType(Transports.socketChannelClass().asSubclass(SocketChannel.class)));
63+
}
64+
65+
}

src/main/java/io/lettuce/core/resource/ClientResources.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.lettuce.core.metrics.CommandLatencyCollectorOptions;
2525
import io.lettuce.core.metrics.CommandLatencyRecorder;
2626
import io.lettuce.core.tracing.Tracing;
27+
import io.netty.resolver.AddressResolverGroup;
2728
import io.netty.util.Timer;
2829
import io.netty.util.concurrent.EventExecutorGroup;
2930
import io.netty.util.concurrent.Future;
@@ -45,10 +46,12 @@
4546
* <li>{@link DnsResolver} to collect latency details. Requires the {@literal LatencyUtils} library.</li>
4647
* <li>{@link Timer} for scheduling</li>
4748
* <li>{@link Tracing} to trace Redis commands.</li>
49+
* <li>{@link AddressResolverGroup} for dns resolution.</li>
4850
* </ul>
4951
*
5052
* @author Mark Paluch
5153
* @author Mikhael Sokolov
54+
* @author Yohei Ueki
5255
* @since 3.4
5356
* @see DefaultClientResources
5457
*/
@@ -241,6 +244,18 @@ default Builder commandLatencyCollector(CommandLatencyCollector commandLatencyCo
241244
*/
242245
Builder tracing(Tracing tracing);
243246

247+
/**
248+
* Sets the {@link AddressResolverGroup} for dns resolution. This option is only effective if
249+
* {@link DnsResolvers#UNRESOLVED} is used as {@link DnsResolver}. Defaults to
250+
* {@link io.netty.resolver.DefaultAddressResolverGroup#INSTANCE} if {@literal netty-dns-resolver} is not available,
251+
* otherwise defaults to {@link io.netty.resolver.dns.DnsAddressResolverGroup}.
252+
*
253+
* @param addressResolverGroup the {@link AddressResolverGroup} instance, must not be {@code null}.
254+
* @return {@code this} {@link Builder}
255+
* @since xxx
256+
*/
257+
Builder addressResolverGroup(AddressResolverGroup<?> addressResolverGroup);
258+
244259
/**
245260
* @return a new instance of {@link DefaultClientResources}.
246261
*/
@@ -385,4 +400,12 @@ default Builder commandLatencyCollector(CommandLatencyCollector commandLatencyCo
385400
*/
386401
Tracing tracing();
387402

403+
/**
404+
* Return the {@link AddressResolverGroup} instance for dns resolution.
405+
*
406+
* @return the address resolver group.
407+
* @since xxx
408+
*/
409+
AddressResolverGroup<?> addressResolverGroup();
410+
388411
}

src/main/java/io/lettuce/core/resource/DefaultClientResources.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import io.lettuce.core.metrics.MetricCollector;
3636
import io.lettuce.core.resource.Delay.StatefulDelay;
3737
import io.lettuce.core.tracing.Tracing;
38+
import io.netty.resolver.AddressResolverGroup;
3839
import io.netty.util.HashedWheelTimer;
3940
import io.netty.util.Timer;
4041
import io.netty.util.concurrent.DefaultEventExecutorGroup;
@@ -70,9 +71,11 @@
7071
* <li>a {@code socketAddressResolver} which is a provided instance of {@link SocketAddressResolver}.</li>
7172
* <li>a {@code timer} that is a provided instance of {@link io.netty.util.HashedWheelTimer}.</li>
7273
* <li>a {@code tracing} that is a provided instance of {@link Tracing}.</li>
74+
* <li>a {@code addressResolverGroup} that is a provided instance of {@link AddressResolverGroup}.</li>
7375
* </ul>
7476
*
7577
* @author Mark Paluch
78+
* @author Yohei Ueki
7679
* @since 3.4
7780
*/
7881
public class DefaultClientResources implements ClientResources {
@@ -103,6 +106,12 @@ public class DefaultClientResources implements ClientResources {
103106
*/
104107
public static final NettyCustomizer DEFAULT_NETTY_CUSTOMIZER = DefaultNettyCustomizer.INSTANCE;
105108

109+
/**
110+
* Default {@link AddressResolverGroup}.
111+
*/
112+
public static final AddressResolverGroup<?> DEFAULT_ADDRESS_RESOLVER_GROUP = AddressResolverGroupProvider
113+
.addressResolverGroup();
114+
106115
static {
107116

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

148157
private final Tracing tracing;
149158

159+
private final AddressResolverGroup<?> addressResolverGroup;
160+
150161
private volatile boolean shutdownCalled = false;
151162

152163
protected DefaultClientResources(Builder builder) {
@@ -243,6 +254,7 @@ protected DefaultClientResources(Builder builder) {
243254
reconnectDelay = builder.reconnectDelay;
244255
nettyCustomizer = builder.nettyCustomizer;
245256
tracing = builder.tracing;
257+
addressResolverGroup = builder.addressResolverGroup;
246258

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

309321
private Tracing tracing = Tracing.disabled();
310322

323+
private AddressResolverGroup<?> addressResolverGroup = DEFAULT_ADDRESS_RESOLVER_GROUP;
324+
311325
private Builder() {
312326
}
313327

@@ -569,6 +583,25 @@ public Builder tracing(Tracing tracing) {
569583
return this;
570584
}
571585

586+
/**
587+
* Sets the {@link AddressResolverGroup} for dns resolution. This option is only effective if
588+
* {@link DnsResolvers#UNRESOLVED} is used as {@link DnsResolver}. Defaults to
589+
* {@link io.netty.resolver.DefaultAddressResolverGroup#INSTANCE} if {@literal netty-dns-resolver} is not available,
590+
* otherwise defaults to {@link io.netty.resolver.dns.DnsAddressResolverGroup}.
591+
*
592+
* @param addressResolverGroup the {@link AddressResolverGroup} instance, must not be {@code null}.
593+
* @return {@code this} {@link ClientResources.Builder}
594+
* @since xxx
595+
*/
596+
@Override
597+
public Builder addressResolverGroup(AddressResolverGroup<?> addressResolverGroup) {
598+
599+
LettuceAssert.notNull(addressResolverGroup, "AddressResolverGroup must not be null");
600+
601+
this.addressResolverGroup = addressResolverGroup;
602+
return this;
603+
}
604+
572605
/**
573606
* @return a new instance of {@link DefaultClientResources}.
574607
*/
@@ -603,7 +636,7 @@ public DefaultClientResources.Builder mutate() {
603636
.commandLatencyPublisherOptions(commandLatencyPublisherOptions()).dnsResolver(dnsResolver())
604637
.eventBus(eventBus()).eventExecutorGroup(eventExecutorGroup()).reconnectDelay(reconnectDelay)
605638
.socketAddressResolver(socketAddressResolver()).nettyCustomizer(nettyCustomizer()).timer(timer())
606-
.tracing(tracing());
639+
.tracing(tracing()).addressResolverGroup(addressResolverGroup());
607640

608641
builder.sharedCommandLatencyCollector = sharedEventLoopGroupProvider;
609642
builder.sharedEventExecutor = sharedEventExecutor;
@@ -742,4 +775,9 @@ public Tracing tracing() {
742775
return tracing;
743776
}
744777

778+
@Override
779+
public AddressResolverGroup<?> addressResolverGroup() {
780+
return addressResolverGroup;
781+
}
782+
745783
}

src/test/java/io/lettuce/core/resource/DefaultClientResourcesUnitTests.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import io.lettuce.test.TestFutures;
3131
import io.lettuce.test.resource.FastShutdown;
3232
import io.netty.channel.nio.NioEventLoopGroup;
33+
import io.netty.resolver.AddressResolverGroup;
3334
import io.netty.util.HashedWheelTimer;
3435
import io.netty.util.Timer;
3536
import io.netty.util.concurrent.EventExecutorGroup;
@@ -39,6 +40,7 @@
3940
* Unit tests for {@link DefaultClientResources}.
4041
*
4142
* @author Mark Paluch
43+
* @author Yohei Ueki
4244
*/
4345
class DefaultClientResourcesUnitTests {
4446

@@ -108,16 +110,19 @@ void testProvidedResources() {
108110
EventBus eventBusMock = mock(EventBus.class);
109111
CommandLatencyCollector latencyCollectorMock = mock(CommandLatencyCollector.class);
110112
NettyCustomizer nettyCustomizer = mock(NettyCustomizer.class);
113+
AddressResolverGroup<?> addressResolverGroup = mock(AddressResolverGroup.class);
111114

112115
DefaultClientResources sut = DefaultClientResources.builder().eventExecutorGroup(executorMock)
113116
.eventLoopGroupProvider(groupProviderMock).timer(timerMock).eventBus(eventBusMock)
114-
.commandLatencyRecorder(latencyCollectorMock).nettyCustomizer(nettyCustomizer).build();
117+
.commandLatencyRecorder(latencyCollectorMock).nettyCustomizer(nettyCustomizer)
118+
.addressResolverGroup(addressResolverGroup).build();
115119

116120
assertThat(sut.eventExecutorGroup()).isSameAs(executorMock);
117121
assertThat(sut.eventLoopGroupProvider()).isSameAs(groupProviderMock);
118122
assertThat(sut.timer()).isSameAs(timerMock);
119123
assertThat(sut.eventBus()).isSameAs(eventBusMock);
120124
assertThat(sut.nettyCustomizer()).isSameAs(nettyCustomizer);
125+
assertThat(sut.addressResolverGroup()).isSameAs(addressResolverGroup);
121126

122127
assertThat(TestFutures.getOrTimeout(sut.shutdown())).isTrue();
123128

@@ -137,11 +142,11 @@ void mutateResources() {
137142
Timer timerMock2 = mock(Timer.class);
138143
EventBus eventBusMock = mock(EventBus.class);
139144
CommandLatencyCollector latencyCollectorMock = mock(CommandLatencyCollector.class);
140-
145+
AddressResolverGroup<?> addressResolverGroupMock = mock(AddressResolverGroup.class);
141146

142147
ClientResources sut = ClientResources.builder().eventExecutorGroup(executorMock)
143148
.eventLoopGroupProvider(groupProviderMock).timer(timerMock).eventBus(eventBusMock)
144-
.commandLatencyRecorder(latencyCollectorMock).build();
149+
.commandLatencyRecorder(latencyCollectorMock).addressResolverGroup(addressResolverGroupMock).build();
145150

146151
ClientResources copy = sut.mutate().timer(timerMock2).build();
147152

@@ -151,6 +156,7 @@ void mutateResources() {
151156
assertThat(sut.timer()).isSameAs(timerMock);
152157
assertThat(copy.timer()).isSameAs(timerMock2).isNotSameAs(timerMock);
153158
assertThat(sut.eventBus()).isSameAs(eventBusMock);
159+
assertThat(sut.addressResolverGroup()).isSameAs(addressResolverGroupMock);
154160

155161
assertThat(TestFutures.getOrTimeout(sut.shutdown())).isTrue();
156162

0 commit comments

Comments
 (0)