88import java .util .LinkedHashMap ;
99import java .util .List ;
1010import java .util .Map ;
11+ import java .util .concurrent .CompletableFuture ;
1112import java .util .concurrent .ExecutionException ;
1213import java .util .concurrent .ExecutorService ;
13- import java .util .concurrent .Future ;
1414import java .util .concurrent .TimeUnit ;
1515import java .util .concurrent .TimeoutException ;
1616import java .util .function .BiFunction ;
@@ -49,7 +49,7 @@ public class InetUtil {
4949 * </ul>
5050 *
5151 * @param ipOrDomainWithPortList list of address strings in {@code ipOrDomain:port} format,
52- * may mix IP literals and domain names
52+ * may mix IP literals and domain names
5353 * @return resolved addresses in the same order as the input, omitting unresolvable entries
5454 */
5555 public static List <InetSocketAddress > resolveInetSocketAddressList (
@@ -71,36 +71,7 @@ public static List<InetSocketAddress> resolveInetSocketAddressList(
7171 }
7272
7373 // Resolve domain names: spin up a thread pool only when there are multiple domains.
74- Map <String , InetSocketAddress > resolvedDomains = new HashMap <>();
75- if (domainEntries .size () > 1 ) {
76- int poolSize = StrictMath .min (domainEntries .size (), DNS_POOL_MAX_SIZE );
77- ExecutorService dnsPool = ExecutorServiceManager
78- .newFixedThreadPool (DNS_POOL_NAME , poolSize , true );
79- List <Future <InetSocketAddress >> futures = new ArrayList <>(domainEntries .size ());
80- for (String entry : domainEntries ) {
81- futures .add (dnsPool .submit (() -> resolveInetSocketAddress (entry )));
82- }
83- for (int i = 0 ; i < domainEntries .size (); i ++) {
84- String entry = domainEntries .get (i );
85- try {
86- resolvedDomains .put (entry ,
87- futures .get (i ).get (DNS_LOOKUP_TIMEOUT_SECONDS , TimeUnit .SECONDS ));
88- } catch (InterruptedException e ) {
89- Thread .currentThread ().interrupt ();
90- logger .warn ("DNS lookup interrupted for: {}" , entry );
91- break ;
92- } catch (ExecutionException e ) {
93- logger .warn ("Failed to resolve address, skip: {}" , entry );
94- } catch (TimeoutException e ) {
95- logger .warn ("DNS lookup timed out after {}s, skip: {}" , DNS_LOOKUP_TIMEOUT_SECONDS ,
96- entry );
97- }
98- }
99- ExecutorServiceManager .shutdownAndAwaitTermination (dnsPool , DNS_POOL_NAME );
100- } else if (domainEntries .size () == 1 ) {
101- String entry = domainEntries .get (0 );
102- resolvedDomains .put (entry , resolveInetSocketAddress (entry ));
103- }
74+ Map <String , InetSocketAddress > resolvedDomains = resolveDomainsInParallel (domainEntries );
10475
10576 // Build the result list preserving the original config order.
10677 for (Map .Entry <String , InetSocketAddress > entry : parsedMap .entrySet ()) {
@@ -116,6 +87,58 @@ public static List<InetSocketAddress> resolveInetSocketAddressList(
11687 return result ;
11788 }
11889
90+ private static Map <String , InetSocketAddress > resolveDomainsInParallel (
91+ List <String > domainEntries ) {
92+ Map <String , InetSocketAddress > resolved = new HashMap <>();
93+ if (domainEntries .isEmpty ()) {
94+ return resolved ;
95+ }
96+
97+ int poolSize = StrictMath .min (domainEntries .size (), DNS_POOL_MAX_SIZE );
98+ ExecutorService dnsPool = ExecutorServiceManager
99+ .newFixedThreadPool (DNS_POOL_NAME , poolSize , true );
100+
101+ try {
102+ LinkedHashMap <String , CompletableFuture <InetSocketAddress >> futures =
103+ new LinkedHashMap <>();
104+ for (String entry : domainEntries ) {
105+ futures .put (entry , CompletableFuture .supplyAsync (
106+ () -> resolveInetSocketAddress (entry ), dnsPool ));
107+ }
108+
109+ // Single global deadline for all lookups combined.
110+ try {
111+ CompletableFuture
112+ .allOf (futures .values ().toArray (new CompletableFuture [0 ]))
113+ .get (DNS_LOOKUP_TIMEOUT_SECONDS , TimeUnit .SECONDS );
114+ } catch (TimeoutException e ) {
115+ logger .warn ("DNS lookup budget {}s exceeded, dropping unresolved entries" ,
116+ DNS_LOOKUP_TIMEOUT_SECONDS );
117+ } catch (InterruptedException e ) {
118+ Thread .currentThread ().interrupt ();
119+ } catch (ExecutionException ignored ) {
120+ // per-entry exceptions handled below
121+ }
122+
123+ // Collect whatever finished; drop pending/failed entries.
124+ for (Map .Entry <String , CompletableFuture <InetSocketAddress >> e : futures .entrySet ()) {
125+ CompletableFuture <InetSocketAddress > f = e .getValue ();
126+ if (f .isDone () && !f .isCompletedExceptionally ()) {
127+ InetSocketAddress addr = f .getNow (null );
128+ if (addr != null ) {
129+ resolved .put (e .getKey (), addr );
130+ }
131+ } else {
132+ logger .warn ("DNS unresolved or timed out, skip: {}" , e .getKey ());
133+ }
134+ }
135+ } finally {
136+ ExecutorServiceManager .shutdownAndAwaitTermination (dnsPool , DNS_POOL_NAME );
137+ }
138+ logger .debug ("DNS look up, src: {}, dst: {}" , domainEntries .size (), resolved .size ());
139+ return resolved ;
140+ }
141+
119142 /**
120143 * Resolves a {@code ipOrDomain:port} config string to an {@link InetSocketAddress} via DNS.
121144 *
0 commit comments