Resolve backend DNS on a bounded thread pool to avoid head-of-line blocking (#1834)

* Use a bounded thread pool for backend DNS resolution

* Queue DNS lookups instead of rejecting when the resolver pool is busy
This commit is contained in:
Clement Raynaud
2026-07-08 11:27:56 +02:00
committed by GitHub
parent 1edab1411d
commit 81a5817a82
@@ -32,17 +32,20 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* An implementation of {@code InetNameResolver} that performs blocking DNS name lookups
* in a separate thread, avoiding blocking the Netty threads for an extended period of time
* and without the downsides of Netty's native DNS resolver.
* on a small bounded pool of separate threads, avoiding blocking the Netty threads for an
* extended period of time and without the downsides of Netty's native DNS resolver.
*/
public final class SeparatePoolInetNameResolver extends InetNameResolver {
private static final int MAX_RESOLVE_THREADS = 8;
private final ExecutorService resolveExecutor;
private final InetNameResolver delegate;
private final Cache<String, List<InetAddress>> cache;
@@ -56,11 +59,15 @@ public final class SeparatePoolInetNameResolver extends InetNameResolver {
*/
public SeparatePoolInetNameResolver(EventExecutor executor) {
super(executor);
this.resolveExecutor = Executors.newSingleThreadExecutor(
ThreadPoolExecutor resolveExecutor = new ThreadPoolExecutor(
MAX_RESOLVE_THREADS, MAX_RESOLVE_THREADS,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(),
new ThreadFactoryBuilder()
.setNameFormat("Velocity DNS Resolver")
.setNameFormat("Velocity DNS Resolver #%d")
.setDaemon(true)
.build());
resolveExecutor.allowCoreThreadTimeOut(true);
this.resolveExecutor = resolveExecutor;
this.delegate = new DefaultNameResolver(executor);
this.cache = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS)