Spring Boot优雅关闭超时未完成任务时如何实现日志记录回调
Spring Boot 优雅关闭超时自定义日志实现方案
你当前使用的是Spring Boot官方提供的标准优雅关闭配置:
server.shutdown=graceful spring.lifecycle.timeout-per-shutdown-phase=1m
要实现等待超时仍有未完成任务时打印自定义日志,完全可以做到,不需要改动原有优雅关闭的核心逻辑,下面是两种低侵入的实现方式:
方式一:容器事件监听(推荐,适配所有内嵌服务器)
Spring容器关闭阶段如果等待超时,会直接中断未完成的生命周期回调走强制退出流程,你可以通过两个步骤实现自定义日志打印:
- 先做一个轻量的运行中任务持有组件,业务代码里处理请求、执行异步任务的时候把任务标识注册进去,任务执行完成就移除标识,不需要侵入核心业务逻辑。
- 写一个最高优先级的
ContextClosedEvent事件监听器,在监听器里判断当前是否还有未完成的任务,如果有就说明触发了关闭超时,直接打印你需要的日志内容。
参考代码:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级,保证日志逻辑在容器销毁前执行 public class ShutdownTimeoutLogListener implements ApplicationListener<ContextClosedEvent> { private static final Logger log = LoggerFactory.getLogger(ShutdownTimeoutLogListener.class); @Autowired private RunningTaskRepository runningTaskRepository; // 自定义的运行中任务存储组件 @Override public void onApplicationEvent(ContextClosedEvent event) { Set<String> unFinishedTasks = runningTaskRepository.getAllRunningTaskIds(); if (!unFinishedTasks.isEmpty()) { log.warn("优雅关闭等待1分钟超时,当前仍存在{}个未完成任务,任务标识:{}", unFinishedTasks.size(), unFinishedTasks); } } }
方式二:自定义内嵌Web服务器的优雅关闭逻辑
如果你只关心Web请求层面的未完成任务,不需要统计自定义异步任务,可以直接扩展对应内嵌服务器的优雅关闭逻辑,在超时分支里加日志,以默认的Tomcat为例:
@Component public class TomcatGracefulShutdownConfig implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> { private static final Logger log = LoggerFactory.getLogger(TomcatGracefulShutdownConfig.class); @Value("${spring.lifecycle.timeout-per-shutdown-phase:1m}") private Duration shutdownTimeout; @Override public void customize(TomcatServletWebServerFactory factory) { factory.addConnectorCustomizers(connector -> { ProtocolHandler protocolHandler = connector.getProtocolHandler(); if (protocolHandler instanceof AbstractProtocol) { AbstractProtocol<?> protocol = (AbstractProtocol<?>) protocolHandler; factory.setGracefulShutdown(() -> { long startTimestamp = System.currentTimeMillis(); // 循环等待活跃请求处理完成 while (System.currentTimeMillis() - startTimestamp < shutdownTimeout.toMillis()) { if (protocol.getActiveCount() == 0) { return; } try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } // 执行到此处说明等待超时 log.warn("Tomcat优雅关闭超时,仍有{}个活跃请求未处理完成,将强制关闭", protocol.getActiveCount()); }); } }); } }
如果使用Jetty、Undertow作为内嵌服务器,实现逻辑一致,只需要替换对应服务器的API获取活跃请求数即可。
补充说明
如果不需要打印具体的未完成任务明细,只是想感知超时事件,直接把org.springframework.boot.lifecycle包的日志级别调整为warn,就能看到框架原生打印的超时提示日志,不需要额外写代码。
所有自定义的关闭逻辑注意控制执行耗时,不要阻塞容器正常退出流程。
内容的提问来源于stack exchange,提问作者Anthony Omiunu
相关产品推荐
相关产品推荐

