讓 Java 程序在特定時間(如每天 17:00)或異常退出後自動重啟,是運維常見需求。本文提供一種「進程內定時檢查 + 外部啟動腳本」的輕量級方案,並分析關鍵陷阱與現代替代方案。
public class AutoRestarter {
private static final Logger log = LoggerFactory.getLogger(AutoRestarter.class);
private static final String RESTART_CMD = "cmd /c start restart.bat"; // Windows
// private static final String RESTART_CMD = "sh restart.sh"; // Linux
private String lastRestartDate = LocalDate.now().toString();
public void scheduleDailyRestart(int hour, int minute) {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
LocalDateTime now = LocalDateTime.now();
String today = now.toLocalDate().toString();
if (!today.equals(lastRestartDate) && now.getHour() == hour && now.getMinute() == minute) {
log.info("觸發每日定時重啟");
lastRestartDate = today;
doRestart();
}
}, 0, 1, TimeUnit.MINUTES);
}
public void onExceptionRestart() {
log.warn("捕獲未處理異常,準備重啟");
doRestart();
}
private void doRestart() {
try {
// 1. 給舊進程 5 秒釋放資源
Thread.sleep(5000);
// 2. 啟動外部腳本
Process p = Runtime.getRuntime().exec(RESTART_CMD);
p.waitFor();
// 3. 退出當前進程
System.exit(0);
} catch (Exception e) {
log.error("重啟失敗", e);
}
}
}
restart.bat@echo off rem 等待端口完全釋放 timeout /t 5 /nobreak >nul java -Xms512m -Xmx1024m -jar myapp.jar rem 若需守護進程模式,可用循環 :LOOP java -jar myapp.jar timeout /t 10 goto LOOP
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
AutoRestarter restarter = ctx.getBean(AutoRestarter.class);
restarter.scheduleDailyRestart(17, 0); // 每天 17:00
// 全局異常捕獲觸發重啟
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
restarter.onExceptionRestart();
});
}
}
| 陷阱 | 現象 | 解決 |
|---|---|---|
| 端口未釋放 | 新進程 BindException 退出 | 重啟前 Thread.sleep(5000) + 腳本 timeout 5 |
| 重複重啟 | 多實例搶佔端口 | 文件鎖 / PID 文件 / 數據庫標誌位互斥 |
| 僵屍進程 | 舊進程未退出,新進程啟動 | 腳本啟動前 taskkill /PID %OLD_PID% 或 pkill -9 |
| 日志丟失 | 重啟前未刷新緩衝區 | Logback <immediateFlush>true</immediateFlush> 或手動 LoggerContext.stop() |
[Unit] Description=My Java App After=network.target [Service] Type=simple ExecStart=/usr/bin/java -jar /opt/app/myapp.jar Restart=always RestartSec=10 StandardOutput=journal [Install] WantedBy=multi-user.target
restartPolicy: Always + livenessProbe 原生守護進程內定時重啟適合**快速原型、無運維權限環境**;生產系統**務必使用作業系統級/容器級守護進程**,實現關注點分離、可觀測性與高可用。