You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebClient优雅停机遇Connection prematurely closed BEFORE response问题

解决WebClient优雅停机时的PrematureCloseException问题

问题根源

应用收到停机信号后,默认的ReactorResourceFactory会优先于业务Bean销毁,导致Netty连接池提前关闭,此时业务中尚未完成的WebClient请求就会触发PrematureCloseException——你的业务Bean的destroyMethod还没来得及执行,连接池就已经被关闭了。

解决方案

1. 自定义ReactorResourceFactory并控制生命周期

创建独立的ReactorResourceFactory Bean,禁用全局资源,这样可以精准控制它的销毁顺序:

@Bean
public ReactorResourceFactory reactorResourceFactory() {
    ReactorResourceFactory factory = new ReactorResourceFactory();
    factory.setUseGlobalResources(false); // 禁用全局资源,由当前Bean管理连接池和事件循环
    return factory;
}

2. 让WebClient绑定自定义资源工厂

修改WebClient的Bean定义,将自定义的ReactorResourceFactory注入,确保所有WebClient请求使用该工厂管理的资源:

@Bean(name = "webClient")
public WebClient webClient(WebClient.Builder builder, ReactorResourceFactory resourceFactory) {
    HttpClient httpClient = HttpClient.create()
            .responseTimeout(Duration.ofSeconds(5))
            .runOn(resourceFactory.getLoopResources()); // 绑定到自定义资源工厂的事件循环

    ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
    return builder.clientConnector(connector).build();
}

3. 控制业务Bean与资源工厂的销毁顺序

通过依赖关系确保业务Bean先销毁:让业务Bean通过构造函数注入WebClient(间接依赖自定义的ReactorResourceFactory),Spring会遵循“初始化顺序逆序销毁”的规则,保证业务Bean先完成销毁逻辑,再销毁ReactorResourceFactory。如果是通过@Bean定义业务类,可添加@DependsOn("webClient")明确依赖关系。

4. 在业务Bean中等待所有请求完成

在业务Bean的销毁方法中,收集并等待所有未完成的WebClient请求执行完毕:

@Component
public class BusinessService {
    private final WebClient webClient;
    private final CopyOnWriteArrayList<Mono<?>> pendingRequests = new CopyOnWriteArrayList<>();

    public BusinessService(@Qualifier("webClient") WebClient webClient) {
        this.webClient = webClient;
    }

    public Mono<Void> executeHttpRequest() {
        Mono<Void> requestMono = webClient.put()
                .uri("http://xxx")
                .retrieve()
                .bodyToMono(Void.class)
                .doFinally(signal -> pendingRequests.remove(requestMono));
        
        pendingRequests.add(requestMono);
        return requestMono;
    }

    @PreDestroy
    public void gracefulShutdown() {
        if (!pendingRequests.isEmpty()) {
            // 等待所有请求完成,设置超时时间避免无限阻塞
            Mono.when(pendingRequests).block(Duration.ofSeconds(30));
        }
    }
}

5. 配置全局停机超时

在application.yml中设置Spring Boot的停机超时时间,给足够时间处理剩余请求:

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

验证效果

触发停机时,业务Bean的@PreDestroy方法会优先执行,等待所有pending的WebClient请求完成后,ReactorResourceFactory才会销毁Netty连接池,从而彻底避免PrematureCloseException。

内容的提问来源于stack exchange,提问作者user143715

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 19:20:09