Skip to content

HttpClient with a LoadBalancer uses only one IP for a multi-homed host #6200

Description

@phraktle

Version

5.1.2

Context

An HttpClient configured with a LoadBalancer is designed to resolve all of a host's A-records and balance requests across them (HttpClientImpl: resolveAll = loadBalancer != null; OriginResolver builds an OriginServer per resolved IP, all available). In practice it connects to a single IP for a multi-homed host.

Notes

  • ((VertxInternal) vertx).nameResolver().resolveAll("www.google.com") returns all 8 addresses, so DNS resolution is fine — the issue is downstream in the built-in origin-resolution → endpoint → selectServer path.
  • Workaround: supplying an explicit AddressResolver (e.g. AddressResolver.mappingResolver(...) with the resolved IPs) + the same LoadBalancer does round-robin across all IPs. So only the built-in (no-custom-resolver) path is affected.

Steps to reproduce

Reproducer

Vertx vertx = Vertx.vertx();
HttpClient client = vertx.httpClientBuilder()
    .with(new HttpClientOptions().setProtocolVersion(HttpVersion.HTTP_1_1).setSsl(true))
    .withLoadBalancer(LoadBalancer.ROUND_ROBIN)   // no custom AddressResolver
    .build();

Set<String> ips = ConcurrentHashMap.newKeySet();
CountDownLatch latch = new CountDownLatch(24);
for (int i = 0; i < 24; i++) {
  client.request(new RequestOptions().setMethod(HttpMethod.GET)
          .setHost("www.google.com").setPort(443).setURI("/").setSsl(true))   // 8 A-records
      .compose(req -> req.send().compose(HttpClientResponse::body).map(b -> req.connection()))
      .onComplete(ar -> { if (ar.succeeded()) ips.add(ar.result().remoteAddress().hostAddress()); latch.countDown(); });
}
latch.await(40, TimeUnit.SECONDS);
System.out.println("distinct remote IPs = " + ips.size());   // prints 1, despite 8 A-records

Expected

Requests spread across the host's resolved IPs per the policy (≈8 distinct remote IPs).

Actual

All requests hit one IP. Same with ROUND_ROBIN, RANDOM, and LEAST_REQUESTS — and RANDOM is stateless, so the load balancer is selecting among a single server; the resolved address set never reaches the selector.

Do you have a reproducer?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions