Aeron代理需SIGKILL才能终止,SpringBoot优雅关闭问题排查
问题描述
基于SpringBoot开发Aeron发布/接收端服务,已通过Runtime.getRuntime().addShutdownHook添加关闭钩子,触发ShutdownSignalBarrier启动优雅关闭流程:
onStop()方法等待信号后执行关闭序列close()方法负责释放所有资源避免泄漏
但捕获关闭信号并执行完关闭流程后,应用仍无法正常终止。相关代码如下:
private Aeron aeron; private AeronArchive aeronArchive; private Publication publication; private Subscription subscription; private AgentRunner sendAgentRunner; private AgentRunner receiveAgentRunner; private SimReceiveAgent receiveAgent; private SimSendAgent sendAgent; @PostConstruct public void init() { setup(); receiveAgent = new SimReceiveAgent(subscription, barrier); sendAgent = new SimSendAgent(publication, aeron); // Construct the subs and pubs Runners sendAgentRunner = new AgentRunner(idleStrategySend, Throwable::printStackTrace, null, sendAgent); receiveAgentRunner = new AgentRunner(idleStrategyReceive, Throwable::printStackTrace, null, receiveAgent); // Start the subs and pubs LOGGER.info("Starting publisher simulator sender thread"); AgentRunner.startOnThread(sendAgentRunner); LOGGER.info("Starting publisher simulator receiver thread"); AgentRunner.startOnThread(receiveAgentRunner); Runtime.getRuntime().addShutdownHook(new Thread(() -> { LOGGER.info("Shutdown signal received, signaling barrier..."); barrier.signal(); })); } public void onStop() throws Exception { barrier.await(); } public void close() throws Exception { barrier.await(); LOGGER.info("Attempt to stop application"); if (timer != null) { timer.cancel(); } CloseHelper.quietClose(sendAgentRunner); CloseHelper.quietClose(receiveAgentRunner); CloseHelper.quietClose(aeron); CloseHelper.quietClose(aeronArchive); }
排查与解决步骤
1. 检查Agent的onClose()实现
AgentRunner.startOnThread()启动的线程会持续运行,直到Agent的onClose()被调用且正确终止核心逻辑。如果你的SimSendAgent和SimReceiveAgent没有实现Agent接口的onClose()方法,或者方法内没有设置终止标记,AgentRunner无法正常停止,线程会一直存活。
确保Agent实现类满足以下要求:
- 定义线程安全的终止标记(如
volatile boolean isRunning) - 在
onClose()中切换终止标记 - 核心处理循环中检查标记,一旦为false就退出并返回
Agent.AGENT_TERMINATED
示例:
public class SimSendAgent implements Agent { private volatile boolean isRunning = true; // 其他成员变量... @Override public void onClose() { isRunning = false; } @Override public int doWork() throws Exception { if (!isRunning) { return Agent.AGENT_TERMINATED; // 通知Runner终止 } // 原有发送逻辑... return 0; } }
2. 消除ShutdownHook与Spring生命周期的冲突
SpringBoot的onStop()(若实现SmartLifecycle接口)本身会在应用关闭时被调用,你通过ShutdownHook触发barrier,同时onStop()又等待barrier,可能导致执行顺序混乱或阻塞。
建议调整逻辑:
- 移除
Runtime.getRuntime().addShutdownHook,直接在onStop()中调用close()方法 - 确保
close()内的资源释放顺序正确:先关闭AgentRunner,再关闭Aeron和AeronArchive(当前代码顺序正确,但需确认CloseHelper.quietClose(AgentRunner)能真正终止线程)
3. 标记Agent线程为守护线程
AgentRunner.startOnThread()默认启动非守护线程,这类线程未终止时JVM不会退出。可以手动将线程设置为守护线程:
// 替换原AgentRunner.startOnThread()代码 Thread sendThread = new Thread(sendAgentRunner); sendThread.setDaemon(true); sendThread.start(); Thread receiveThread = new Thread(receiveAgentRunner); receiveThread.setDaemon(true); receiveThread.start();
4. 替换ShutdownSignalBarrier为可靠实现
若自定义的ShutdownSignalBarrier存在唤醒逻辑缺陷,会导致onStop()或close()一直阻塞。建议用CountDownLatch替代,它的线程安全性和唤醒可靠性更有保障:
// 替换原barrier定义 private final CountDownLatch shutdownLatch = new CountDownLatch(1); // 触发关闭时调用 shutdownLatch.countDown(); // 等待关闭信号时调用 shutdownLatch.await();
5. 排查未释放的其他资源
除代码中列出的资源,可能还有未关闭的后台线程(如timer关联的线程池、自定义线程等)导致JVM无法退出。可以用以下命令排查存活线程:
jstack <应用进程ID>
定位未终止的线程后,找到对应的资源并添加关闭逻辑。
内容的提问来源于stack exchange,提问作者Diptaneal Roy

