如何在SpringBoot关闭时暂停应用,让Prometheus采集最后15秒指标?
我们的SpringBoot应用使用Micrometer生成指标,Prometheus每15秒采集一次。当应用关闭(如Pod重启)时,可能会丢失最后15秒的指标数据。除推送指标、持久化指标等方案外,我想尝试在关闭时暂停应用:停止运行业务代码(避免计数器继续增长),同时保持/actuator/prometheus端点可用,等待Prometheus完成最后一次采集。
我曾尝试在@PreDestroy中使用Thread.sleep(),也试过设置标记,但无论用哪种钩子,操作都为时已晚——/actuator/prometheus已经关闭。请问如何实现这种暂停逻辑?是否有触发足够早的钩子能确保Actuator端点仍正常工作?
1. 用SmartLifecycle控制关闭顺序
SmartLifecycle能自定义Bean的启动/关闭优先级,你可以让业务组件先停止,Actuator相关组件最后关闭,确保Prometheus端点在业务暂停后仍能访问。
实现示例:
首先定义一个业务生命周期管理器,控制业务代码的执行开关,并设置较高的关闭优先级:
@Component public class BusinessLifecycleManager implements SmartLifecycle { private volatile boolean running = false; public volatile boolean allowBusinessProcessing = true; @Override public void start() { running = true; allowBusinessProcessing = true; } @Override public void stop() { // 先禁止业务代码执行 allowBusinessProcessing = false; // 等待15秒,给Prometheus留足采集时间 try { Thread.sleep(15000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } running = false; } @Override public boolean isRunning() { return running; } // 数值越小,关闭优先级越高(先执行) @Override public int getPhase() { return 0; // Actuator默认优先级为2147483647,会在之后关闭 } }
然后在业务代码中注入这个管理器,判断是否允许执行:
@Service public class BusinessService { @Autowired private BusinessLifecycleManager lifecycleManager; public void processBusinessTask() { if (!lifecycleManager.allowBusinessProcessing) { return; // 停止业务逻辑 } // 原有业务代码 // ... } }
2. 监听ContextClosedEvent并延迟上下文关闭
通过监听Spring的ContextClosedEvent,在上下文开始关闭时立即暂停业务,然后延迟一段时间再让关闭流程继续,这段时间内Actuator端点依然可用。
实现示例:
@Order(Ordered.HIGHEST_PRECEDENCE) @Component public class ShutdownDelayListener implements ApplicationListener<ContextClosedEvent> { @Autowired private BusinessLifecycleManager lifecycleManager; @Override public void onApplicationEvent(ContextClosedEvent event) { // 立即暂停业务 lifecycleManager.allowBusinessProcessing = false; // 延迟15秒,等待Prometheus完成最后一次采集 try { Thread.sleep(15000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 延迟结束后,上下文会继续执行关闭流程 } }
@Order(Ordered.HIGHEST_PRECEDENCE)确保这个监听器最先触发,早于Actuator的关闭逻辑。
3. 自定义Prometheus端点的关闭时机
通过自定义EndpointShutdownHandler,单独控制Prometheus端点的关闭时间,让它在最后才关闭:
@Configuration public class ActuatorShutdownConfig { @Bean public EndpointShutdownHandler prometheusEndpointShutdownHandler() { return (context, endpointId) -> { if ("prometheus".equals(endpointId)) { // 延迟15秒再关闭Prometheus端点 try { Thread.sleep(15000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }; } }
配合业务逻辑的暂停机制,就能实现业务停止后,Prometheus端点仍能提供指标的效果。
内容的提问来源于stack exchange,提问作者R2C2

