Java轮询停止检测:60秒无运行时触发告警方案
实现心跳监控与告警方案
核心思路
启动一个独立的监控线程,定期检查主轮询循环的心跳时间戳:
- 主循环每次迭代时更新线程安全的心跳时间戳
- 监控线程每隔固定时间(如10秒)检查时间差,若超过60秒未收到心跳则触发告警
具体实现代码
1. 定义共享心跳变量
用AtomicLong保证多线程环境下的读写原子性:
import java.util.concurrent.atomic.AtomicLong; // 共享心跳时间戳,初始化为当前时间 private static final AtomicLong lastHeartbeat = new AtomicLong(System.currentTimeMillis());
2. 修改主轮询循环
在每次循环迭代时更新心跳,确保正常执行或捕获到异常(循环未终止)时都标记存活:
for (;;) { try { // 进入循环立即更新心跳 lastHeartbeat.set(System.currentTimeMillis()); // 1. 轮询消息流逻辑 // 2. 处理消息逻辑 // 3. 存储至数据库逻辑 // 计算执行耗时,调整睡眠时间(保证总周期约20秒) long executionCost = System.currentTimeMillis() - lastHeartbeat.get(); long sleepDuration = Math.max(0, 20000 - executionCost); Thread.sleep(sleepDuration); } catch (Exception e) { // 4. 异常处理逻辑(打日志、重试等) // 即使发生异常,只要循环未终止,就更新心跳 lastHeartbeat.set(System.currentTimeMillis()); } }
3. 启动监控定时任务
用ScheduledExecutorService实现定期检查,触发告警逻辑:
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class PollingMonitor { public static void startMonitor() { ScheduledExecutorService monitorService = Executors.newSingleThreadScheduledExecutor(runnable -> { Thread thread = new Thread(runnable); // 设置为守护线程,避免阻止程序正常退出 thread.setDaemon(true); // 设置未捕获异常处理器,防止监控线程意外挂掉 thread.setUncaughtExceptionHandler((t, e) -> { System.err.println("监控线程异常: " + e.getMessage()); e.printStackTrace(); }); return thread; }); // 初始延迟0秒,每隔10秒检查一次 monitorService.scheduleAtFixedRate(() -> { long currentTime = System.currentTimeMillis(); long lastBeatTime = lastHeartbeat.get(); // 超过60秒未收到心跳,触发告警 if (currentTime - lastBeatTime > 60 * 1000) { sendAlert("轮询任务已停止超过60秒,最后心跳时间: " + lastBeatTime); } }, 0, 10, TimeUnit.SECONDS); } // 告警实现:发送邮件示例 private static void sendAlert(String alertMsg) { // 此处替换为你的邮件发送逻辑 try { // 示例:使用JavaMail API发送邮件(需引入javax.mail依赖) javax.mail.Session session = javax.mail.Session.getInstance(new java.util.Properties(), null); javax.mail.Message message = new javax.mail.internet.MimeMessage(session); message.setFrom(new javax.mail.internet.InternetAddress("your-alert-sender@example.com")); message.setRecipients(javax.mail.Message.RecipientType.TO, javax.mail.internet.InternetAddress.parse("your-alert-receiver@example.com")); message.setSubject("轮询任务异常告警"); message.setText(alertMsg); javax.mail.Transport.send(message); System.out.println("告警邮件已发送"); } catch (Exception e) { System.err.println("告警发送失败: " + e.getMessage()); e.printStackTrace(); } } }
4. 启动监控
在应用启动时调用PollingMonitor.startMonitor()即可。
关键注意事项
- 线程安全:必须用
AtomicLong或其他线程安全的方式存储心跳时间戳,避免多线程读写冲突。 - 监控线程可靠性:设置守护线程和未捕获异常处理器,防止监控自身挂掉导致告警失效。
- 告警阈值调整:若任务单次执行可能超过60秒(如批量处理大量消息),需适当调高高警阈值,或在任务执行过程中定时更新心跳。
- 资源清理:应用关闭时,需调用
monitorService.shutdown()释放线程池资源。
内容的提问来源于stack exchange,提问作者Om Sao
相关产品推荐
相关产品推荐

