首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java程序自動重啟實現方案:定時重啟與異常自愈

     編輯:Java 相關知識

讓 Java 程序在特定時間(如每天 17:00)或異常退出後自動重啟,是運維常見需求。本文提供一種「進程內定時檢查 + 外部啟動腳本」的輕量級方案,並分析關鍵陷阱與現代替代方案。

一、核心難點

  1. 自我重啟悖論:JVM 進程結束後無法自拉起,需外部進程協助
  2. 端口釋放競態:舊進程端口未釋放,新進程綁定失敗 → 立即退出
  3. 重複重啟風險:重啟邏輯未加鎖,可能並發啟動多實例

二、輕量級實現方案

1. 核心邏輯 (運行在主程序內部)

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);
        }
    }
}

2. 啟動腳本 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

3. 整合入口

@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()

四、生產級替代方案(強烈推薦)

  1. Systemd (Linux):
    [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
  2. Supervisor:跨平台進程管理,Web 界面監控
  3. NSSM (Windows):將 Java 程序安裝為 Windows 服務,自動恢復
  4. Kubernetes:restartPolicy: Always + livenessProbe 原生守護
  5. Spring Boot Admin + Actuator:健康檢查異常觸發告警,配合外部調度器重啟

五、總結

進程內定時重啟適合**快速原型、無運維權限環境**;生產系統**務必使用作業系統級/容器級守護進程**,實現關注點分離、可觀測性與高可用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved