How to Implement Load‑Balancing with a Custom Java DNS Resolver
This article explains why the default InMemoryDnsResolver cannot provide load balancing, shows how to create a custom SystemDefaultDnsResolver that randomly selects IPs, and demonstrates single‑thread, multi‑thread, and single‑connection pool tests to verify the behavior.
Why InMemoryDnsResolver Cannot Provide Load Balancing
The built‑in org.apache.http.impl.conn.InMemoryDnsResolver keeps host‑to‑IP mappings in a ConcurrentHashMap<String, InetAddress[]>. The add method stores the mapping once, and subsequent resolve calls simply return the stored array. Because the map never changes after the first insertion, the resolver always returns the same IP for a given host, making it unsuitable for per‑request load balancing.
private static DnsResolver getDnsResolver2() {
InMemoryDnsResolver dnsResolver = new InMemoryDnsResolver();
try {
logger.warn("调用一次");
dnsResolver.add("fun.tester", InetAddress.getByName("127.0.0.1"));
} catch (Exception e) {
e.printStackTrace();
}
return dnsResolver;
}Custom Resolver Based on SystemDefaultDnsResolver
To achieve random IP selection, the author overrides org.apache.http.impl.conn.SystemDefaultDnsResolver. The overridden resolve method checks whether the host equals "fun.tester"; if so, it returns a single randomly chosen address from a pre‑initialized list, otherwise it delegates to the superclass (which uses the OS resolver).
private static DnsResolver getDnsResolver() {
return new SystemDefaultDnsResolver() {
@Override
public InetAddress[] resolve(final String host) throws UnknownHostException {
if (host.equalsIgnoreCase("fun.tester")) {
// SourceCode.random picks a random element from the list "ips"
return new InetAddress[]{SourceCode.random(ips)};
} else {
return super.resolve(host);
}
}
};
}The static list ips holds the candidate addresses and is built once at startup:
private static List<InetAddress> getAddress() {
try {
return Arrays.asList(
InetAddress.getByName("127.0.0.1"),
InetAddress.getByName("0.0.0.0")
);
} catch (Exception e) {
FailException.fail("DNS IP解析失败!");
}
return null;
}Because a custom resolver replaces the default lookup, the system /etc/hosts file is ignored. Use this approach only when bypassing the host file is acceptable.
Test Scenarios
Single‑Thread Request
A loop sends ten HTTP GET requests to http://fun.tester:12345/. The console shows that DNS resolution occurs only once; the returned IP alternates between the two configured addresses, which does not satisfy per‑request load balancing.
public static void main(String[] args) {
String url = "http://fun.tester:12345/";
HttpGet get = getHttpGet(url);
Runnable test = () -> getHttpResponse(get);
for (int i = 0; i < 10; i++) {
test.run();
}
}Multi‑Thread Request
Ten parallel threads execute the same request. Each thread invokes SystemDefaultDnsResolver#resolve and receives a randomly chosen IP. However, every thread creates a new TCP connection, so request latency is noticeably higher.
public static void main(String[] args) {
String url = "http://fun.tester:12345/";
HttpGet get = getHttpGet(url);
Runnable test = () -> {
fun(() -> getHttpResponse(get));
};
for (int i = 0; i < 10; i++) {
new Thread(test).start();
}
}Single Connection Pool
The HttpClient connection pool is limited to a single connection:
public static int MAX_PER_ROUTE_CONNECTION = 1;
public static int MAX_TOTAL_CONNECTION = 1;Even with ten concurrent threads, all requests share the same underlying connection. DNS resolution is performed only once, and each request still incurs the cost of waiting for the shared connection, resulting in uniformly long response times.
Key Takeaways
InMemoryDnsResolver stores a static map; after the first add call the same IP is always returned, so it cannot provide load balancing.
Overriding SystemDefaultDnsResolver allows random selection of IPs for specific hosts while preserving the default OS resolution for all others.
A custom DNS resolver disables the normal /etc/hosts lookup; this side‑effect must be considered.
Effective load balancing requires both random DNS resolution and connection reuse. Limiting the HttpClient pool to a single connection forces every request to wait for a new connection, negating the benefit of random DNS selection.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
