WebClient线程未停止致应用关闭内存泄漏问题求助
我的应用中有一个端点通过WebClient调用3个REST API,运行时可正常获取所有接口响应,但在本地停止应用时出现内存泄漏相关错误,错误日志如下:
o.a.c.loader.WebappClassLoaderBase : The web application [ROOT] appears to have started a thread named [reactor-http-nio-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread: sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method) sun.nio.ch.WindowsSelectorImpl$SubSelector.poll(WindowsSelectorImpl.java:296) sun.nio.ch.WindowsSelectorImpl$SubSelector.access$400(WindowsSelectorImpl.java:278) sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:159) sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62) io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:883) io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:526) io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) java.lang.Thread.run(Thread.java:750)
该错误共出现4次,推测与WebClient的调用逻辑相关。以下是其中一个端点的调用代码:
public Void makeRestCall(Request request) { URI endpoint = UriComponentsBuilder.fromUriString("url").build().encode().toUri(); return webClient.post().uri(endpoint).contentType(MediaType.APPLICATION_JSON) .bodyValue(request) .retrieve() .onStatus(HttpStatus::is4xxClientError, stringResponse -> stringResponse.bodyToMono(Fault.class).flatMap( pojoResponse -> stringResponse.createException().flatMap(ex -> Mono.error( new HttpClientErrorException(ex.getStatusCode(), "Field: " + pojoResponse.getErrors().get(0) .getSourceProperty() + " - Error: " + pojoResponse.getErrors().get(0).getMessageID() ))))) .onStatus(HttpStatus::is5xxServerError, stringResponse -> stringResponse.bodyToMono(Fault.class).flatMap( pojoResponse -> stringResponse.createException().flatMap(ex -> Mono.error( new HttpClientErrorException(ex.getStatusCode(), pojoResponse.getMessage()))))) .toBodilessEntity().block().getBody(); }
不确定问题原因,是否需要手动关闭WebClient?原以为Spring容器会自动处理,恳请协助解决。
问题原因
WebClient默认依赖Netty作为底层HTTP客户端,调用block()会触发Netty创建NIO线程池(如日志中的reactor-http-nio-1)。如果WebClient不是Spring托管的Bean,或底层Netty资源未配置销毁逻辑,Spring容器停止时无法自动关闭这些线程,导致线程残留引发内存泄漏告警。
解决步骤
确保WebClient为Spring托管Bean
不要手动实例化WebClient,通过WebClient.Builder创建并注入:@Bean public WebClient webClient(WebClient.Builder builder) { return builder.build(); }此方式下Spring会在容器销毁时自动处理WebClient的资源释放。
配置Netty资源的自动销毁逻辑
若使用自定义HttpClient,需指定销毁方法让Spring自动清理Netty线程池:@Bean(destroyMethod = "dispose") public HttpClient httpClient() { return HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5)) .addHandlerLast(new WriteTimeoutHandler(5))); } @Bean public WebClient webClient(HttpClient httpClient) { return WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build(); }通过
destroyMethod = "dispose",Spring会在容器关闭时调用HttpClient的销毁方法,关闭Netty线程资源。规范响应式调用方式
你的代码使用block()将响应式调用转为同步,易导致线程资源脱离Spring管理。如果是Spring WebFlux应用,建议全程使用响应式编程(返回Mono<Void>而非直接block);如果是Spring MVC应用,确保WebClient Bean被正确管理的同时,避免不必要的同步阻塞。
结论
无需手动关闭WebClient,但必须保证它是Spring托管的Bean,且底层Netty资源配置了正确的销毁逻辑。仅当使用自定义HttpClient时,需通过destroyMethod指定资源清理方式,让Spring自动完成线程池的关闭。
内容的提问来源于stack exchange,提问作者JavaJo

