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

Spring Boot嵌入式Jetty:如何检查待处理请求并实现进程自动退出

优雅关闭Spring Boot嵌入式Jetty服务并终止进程

针对你的需求——在无请求时关闭Spring上下文并退出Java进程,同时确保所有待处理请求完成后再执行操作,我们可以通过以下步骤实现:

核心思路

要实现优雅关闭,需要完成三个关键动作:

  1. 阻止Jetty接受新请求
  2. 等待所有正在处理的请求完成
  3. 关闭Spring上下文、停止Jetty服务并终止进程

步骤1:获取嵌入式Jetty的Server实例

首先需要拿到Jetty的Server对象,后续用来控制请求接受和监控活跃连接。在Spring Boot中,可以通过注入WebServer并转换为Jetty实现:

@Autowired
private WebServer webServer;

private Server getJettyServer() {
    if (webServer instanceof JettyWebServer) {
        return ((JettyWebServer) webServer).getServer();
    }
    throw new IllegalStateException("当前服务未运行在Jetty容器上");
}

步骤2:配置请求统计(可选但推荐)

为了精准监控活跃请求数,建议给Jetty添加StatisticsHandler,它能帮我们直接获取当前处理中的请求数量。通过自定义JettyServletWebServerFactory来配置:

@Bean
public JettyServletWebServerFactory jettyServletWebServerFactory() {
    JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
    factory.addServerCustomizers(server -> {
        // 用StatisticsHandler包装原有的请求处理器
        Handler originalHandler = server.getHandler();
        StatisticsHandler statsHandler = new StatisticsHandler();
        statsHandler.setHandler(originalHandler);
        server.setHandler(statsHandler);
    });
    return factory;
}

步骤3:实现优雅关闭的Shutdown接口

修改你的shutdown接口,加入请求等待、上下文关闭和进程终止逻辑。注意要异步执行关闭逻辑,避免当前shutdown请求还未响应就开始关闭服务:

@Autowired
private ApplicationContext context;

@PostMapping("/shutdown")
public CompletableFuture<Void> initiateShutdown() {
    return CompletableFuture.runAsync(() -> {
        try {
            Server jettyServer = getJettyServer();
            final long SHUTDOWN_TIMEOUT = 30000; // 30秒超时,可根据业务调整
            long startTime = System.currentTimeMillis();

            // 1. 停止Jetty接受新请求
            for (Connector connector : jettyServer.getConnectors()) {
                connector.stopAccepting();
                System.out.println("已停止接受新请求");
            }

            // 2. 等待所有活跃请求处理完成
            while (hasActiveRequests(jettyServer) && (System.currentTimeMillis() - startTime) < SHUTDOWN_TIMEOUT) {
                System.out.println("等待剩余请求处理完成... 当前活跃请求数: " + getActiveRequestCount(jettyServer));
                Thread.sleep(500); // 每隔500ms检查一次
            }

            if (hasActiveRequests(jettyServer)) {
                System.err.println("超时仍有活跃请求,强制关闭");
            }

            // 3. 关闭Spring应用上下文
            ((ConfigurableApplicationContext) context).close();
            System.out.println("Spring上下文已关闭");

            // 4. 停止Jetty服务器并等待其完全终止
            jettyServer.stop();
            jettyServer.join();
            System.out.println("Jetty服务器已停止");

            // 5. 终止Java进程
            System.exit(0);
        } catch (Exception e) {
            System.err.println("优雅关闭失败: " + e.getMessage());
            e.printStackTrace();
            System.exit(1);
        }
    });
}

// 检查是否有活跃请求
private boolean hasActiveRequests(Server jettyServer) {
    return getActiveRequestCount(jettyServer) > 0;
}

// 获取当前活跃请求数
private long getActiveRequestCount(Server jettyServer) {
    Handler handler = jettyServer.getHandler();
    if (handler instanceof StatisticsHandler) {
        return ((StatisticsHandler) handler).getActiveRequests();
    }

    // 备用方案:统计活跃连接数(适用于未配置StatisticsHandler的情况)
    for (Connector connector : jettyServer.getConnectors()) {
        ConnectionManager connectionManager = connector.getConnectionManager();
        if (connectionManager instanceof AbstractConnectionPool) {
            return ((AbstractConnectionPool) connectionManager).getConnections().size();
        }
    }
    return 0;
}

关键说明

  • 为什么之前的代码无法终止进程?:仅关闭Spring上下文不会停止Jetty的后台线程池(比如接受连接的线程、请求处理线程),这些线程会保持进程存活,因此需要显式调用jettyServer.stop()并终止进程。
  • 异步执行关闭逻辑:如果同步执行,当前的shutdown请求会在关闭过程中被中断,客户端会收到异常响应,用CompletableFuture异步执行可以保证请求正常响应后再开始关闭。
  • 超时机制:添加超时避免因异常情况导致无限等待,超时后强制关闭服务。
  • 安全建议:务必给/shutdown接口添加权限校验(比如API密钥、Spring Security认证),防止恶意调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:35:57