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

Jetty HttpClient触发SIGTERM时抛出AsynchronousCloseException问题排查

解决Jetty HttpClient优雅停机时的AsynchronousCloseException问题

我之前在做Jetty HttpClient优雅停机的时候,也踩过这个AsynchronousCloseException的坑,结合实战经验给你梳理下问题根源和具体解决步骤:

问题根源

当你用kill命令触发ShutdownHook时,如果直接粗暴调用HttpClient的停止方法,底层的NIO通道会被强制关闭,而此时还有正在处理的异步请求在进行IO操作,就会抛出这个异常。本质是停机流程没有给正在执行的请求留足完成时间,也没有正确区分“正常停机关闭”和“异常错误”。

具体解决办法

1. 先拦截新请求,避免后续任务涌入

先维护一个全局的停机状态标记,在提交任何新请求前做检查,确保停机过程中不再接收新任务:

private static final AtomicBoolean IS_SHUTTING_DOWN = new AtomicBoolean(false);

// 提交请求前先判断
if (IS_SHUTTING_DOWN.get()) {
    throw new IllegalStateException("System is shutting down, cannot accept new requests");
}
httpClient.send(request, responseListener);

2. 优化ShutdownHook中的停机流程

不要直接调用httpClient.stop(),而是按顺序关闭组件,给正在处理的请求留足完成时间:

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    IS_SHUTTING_DOWN.set(true);
    LOGGER.info("Starting graceful shutdown of Jetty HttpClient...");

    try {
        // 先关闭执行器,停止接受新任务
        httpClient.getExecutor().shutdown();
        // 关闭调度器
        httpClient.getScheduler().shutdown();

        // 设置超时时间,比如30秒,让正在处理的请求完成
        long timeout = 30;
        // 优雅停止HttpClient
        httpClient.stop(timeout, TimeUnit.SECONDS);

        // 等待执行器终止,超时则强制关闭
        if (!httpClient.getExecutor().awaitTermination(timeout, TimeUnit.SECONDS)) {
            LOGGER.warn("Executor did not terminate in time, forcing shutdown");
            httpClient.getExecutor().shutdownNow();
        }

        LOGGER.info("Jetty HttpClient gracefully shut down");
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        LOGGER.error("Graceful shutdown interrupted", e);
    } catch (Exception e) {
        LOGGER.error("Failed to gracefully shut down HttpClient", e);
    }
}));

3. 在响应Listener中区分异常类型

在你注册的请求/响应Listener里,捕获AsynchronousCloseException,判断是否是停机导致的,避免误报:

Response.CompleteListener responseListener = new Response.CompleteListener() {
    @Override
    public void onComplete(Result result) {
        if (result.isFailed()) {
            Throwable failure = result.getFailure();
            if (failure instanceof AsynchronousCloseException && IS_SHUTTING_DOWN.get()) {
                // 这是停机过程中的正常关闭,无需告警
                LOGGER.info("Request closed during graceful shutdown: {}", result.getRequest().getURI());
            } else {
                // 处理真正的业务异常
                LOGGER.error("Request failed: {}", result.getRequest().getURI(), failure);
            }
        } else {
            // 正常处理响应
            LOGGER.info("Request succeeded: {}", result.getRequest().getURI());
        }
    }
};

4. 调整HttpClient的连接池配置

合理配置连接池参数,减少停机时的资源占用和异常概率:

HttpClient httpClient = new HttpClient();
// 设置连接空闲超时,避免空闲连接占用资源
httpClient.setIdleTimeout(5000);
// 配置连接池
ConnectionPool pool = new QueuedThreadPoolConnectionPool();
pool.setMaxConnectionsPerDestination(100);
pool.setIdleTimeout(5000);
httpClient.setConnectionPool(pool);

测试验证

测试的时候可以模拟10-20个并发请求,然后执行kill -TERM <进程ID>,观察日志:

  • 新请求会被拦截
  • 正在处理的请求能正常完成
  • AsynchronousCloseException被正确识别为正常停机事件,不会触发错误告警

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:15:27