Sunday, April 26, 2026

Spring WebClient Failed to Resolve Example

Have you ever encountered Spring's WebClient throwing a "failed to resolve 'hostname' after x queries" error?. Well, if you ran my Crowsnest project prior to my latest merge, then chances are you may have. Especially, if you ran it inside your internal network. Here's what the error looks like.

Spring WebClient Failed to Resolve Cause

As the error states, the target hostname can't be resolved. It is a DNS thing. By default WebClient uses the JVM implementation to resolve the hostname. In my case, my Linux box was inside a VPN and somehow Java's standard networking library (e.g. java.net.InetAddress) was ignoring the local DNS settings. Worked fine when the target was accessible in the Internet.

Spring WebClient Failed to Resolve Sorted

To solve this dilemma, we simple change the default hostname resolver to something that can do the job. Luckily, there is Netty's DNS resolver we can use. It is io.netty.resolver.DefaultAddressResolverGroup, and we just tell Spring to use it. So WebClient will now use Netty's DNS Resovler thru HttpClient and no longer the default JVM implementation. We'll need to change a couple of things in our code.

Create a new WebClient that will be injected in other parts of the code.


package net.codesamples.crowsnest;

// ... other imports snipped
import io.netty.resolver.DefaultAddressResolverGroup;
import reactor.netty.http.client.HttpClient;

@SpringBootApplication
@EnableScheduling
public class CrowsnestApplication {

	public static void main(String[] args) {
		SpringApplication.run(CrowsnestApplication.class, args);
	}

	@Bean
	public WebClient webClient() {
		HttpClient httpClient = HttpClient.create().resolver(DefaultAddressResolverGroup.INSTANCE);
		return WebClient.builder()
				.clientConnector(new ReactorClientHttpConnector(httpClient)).build();
	}
}

Use the bean in other parts of the code thru @Autowired.


package net.codesamples.crowsnest;

// ... impoerts snipped

@Component
public class ScheduledPings {
    private static final Logger log = LoggerFactory.getLogger(ScheduledPings.class);

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    @Autowired
    EnvironmentConfigurationWatcher environmentConfigurationWatcher;

    @Autowired
    SimpMessagingTemplate simpMessagingTemplate;

    @Autowired
    private WebClient webClient;

    private List internalEnvironmentList = new ArrayList<>();

    private ObjectMapper mapper = new ObjectMapper();

    @Scheduled(cron = "${cron.expression}")
    public void pinger() {
        log.info("The time is now {}", dateFormat.format(new Date()));
        List environmentList = environmentConfigurationWatcher.getEnvironmentList();
        log.info("Environment list: {}", environmentList);

        for (Environment environment : environmentList) {
            for (App app : environment.getApps()) {
                Mono mono = webClient.get()
                        .uri(app.getUrl())
                        .exchangeToMono(clientResponse -> {
                            HttpStatusCode statusCode = clientResponse.statusCode();
                            if (statusCode.is2xxSuccessful()) {
                                app.setStatus("up");
                            }
                            log.info("StatusCode: {} = {}", statusCode, app.getUrl());
                            return clientResponse.bodyToMono(String.class);
                        })
                        .onErrorResume(Exception.class, exception -> {
                            log.info("Exception {}", exception.getMessage());
                            app.setStatus("down");
                            return Mono.empty();
                        });
                mono.subscribe();
            }
        }

// ... code snipped
}

Spring WebClient Failed to Resolve Fixed

Things should now work again. If it does not, let me know.

I need more WebClients...