溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

防止JAVA程序重復(fù)啟動的解決辦法

發(fā)布時間:2021-12-02 18:40:18 來源:億速云 閱讀:269 作者:柒染 欄目:編程語言

防止JAVA程序重復(fù)啟動的解決辦法,針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

我們項目中有一個后臺任務(wù)處理程序,是java開發(fā)application,用以處理網(wǎng)站提交的一些批量數(shù)據(jù)文件,因為這些數(shù)據(jù)文件數(shù)據(jù)量一般都比較大,所以寫了這個批量處理程序,用以異步處理這些批量數(shù)據(jù)文件。這個程序設(shè)計成插件式的,處理各種不同數(shù)據(jù)文件的功能單獨作為一個插件,然后使用Spring來粘合各個組件,這樣就可以很方便地對該程序進行擴展。
       今天客戶提出一個要求:需要控制這個程序在同一主機上只能啟動一個實例。
       為了實現(xiàn)客戶要求,我首先想到就是在數(shù)據(jù)庫中建一張表,程序啟動時往該表中寫入一個標志,等程序結(jié)束時再刪除標志。但這種方式存在一個問題就是,如果程序是非正常停止或被殺進程,那么這個標志就不可能被清除,那下一次啟動就會誤判為重復(fù)啟動;另外,如果用數(shù)據(jù)庫來記錄啟動標志的話,還把該程序跟數(shù)據(jù)庫緊密耦合起來,感覺很別扭。
       排除了第一種方案之后,我以想到了用文件來保存啟動標志(好象一些大型的程序,諸如weblogic好象就是采用在文件中記錄啟動標志方式來控制重復(fù)啟動的)??土髁咳贿@種方式不需要與數(shù)據(jù)庫耦合在一起,但也存在程序異常中止而無法清除啟動標志的問題,所以這個方案也被槍斃了。
       我想到的第三種方案就是在JAVA中調(diào)用操作系統(tǒng)的查看系統(tǒng)進程的方式來取得系統(tǒng)進程,然后再檢測系統(tǒng)進程有特殊的進程標志來判斷是否重復(fù)啟動。但這種方式一是看起來很別扭,再者就是Window和 *nix系統(tǒng)中查看系統(tǒng)進程的命令不一樣,分成幾種情況來處理,無端地增加了程序的復(fù)雜性,也不可取。
       能不能在內(nèi)存中記錄一個啟動標志呢?理論上這應(yīng)該是不可行的,因為跨JVM來相互操作內(nèi)存數(shù)據(jù)是不可能。我在網(wǎng)上搜了一下,也沒找到相關(guān)的例子。
       那能不能占用一點系統(tǒng)共享資源,來換取我們的目標呢?比較容易想到的系統(tǒng)資源并且不能重復(fù)使用的資源就是端口。我嘗試采用如下方案:在程序中指定一個不常用的端口(比如:12345),在程序啟動時,就指定的端口啟動一個ServerSocket,這個Socket只是為了占用這個端口,不接受任何網(wǎng)絡(luò)連接。如果試圖啟動第二個實例時,程序在該指定端口啟動ServerSocket時就會拋異常,這時我們就可以認為系統(tǒng)已經(jīng)啟動過了,然后打印提示并直接退出程序即可。這種方式在理論上分析應(yīng)該可以的,我開始動手修改程序。程序修改如下:

java 代碼

  1. package cn.com.pansky.xmdswz.application.scheduler;  

  2.  

  3. import org.apache.commons.logging.Log;  

  4. import org.apache.commons.logging.LogFactory;  

  5. import org.quartz.SchedulerException;  

  6. import org.quartz.impl.StdScheduler;  

  7. import org.springframework.beans.factory.BeanFactory;  

  8. import org.springframework.context.support.ClassPathXmlApplicationContext;  

  9. import cn.com.pansky.xmdswz.system.cache.CachedTableMgr;  

  10. import cn.com.pansky.xmdswz.system.config.SystemConfig;  

  11. import cn.com.pansky.xmdswz.utility.DateUtil;  

  12. import org.quartz.JobDetail;  

  13. import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;  

  14. import java.net.ServerSocket;  

  15. import java.io.*;  

  16.  

  17. /**

  18. * Title: XXXXXXX

  19. * Description: XXXXXXXXXXXX

  20. * Copyright: Copyright (c) 2006

  21. * Company: www.pansky.com.cn

  22. *

  23. * @author Sheng Youfu

  24. * @version 1.0

  25. */  

  26. publicclass Scheduler {  

  27.  privatestatic Log log = LogFactory.getLog(Scheduler.class);  

  28.  

  29.  privatestatic ServerSocket srvSocket = null; //服務(wù)線程,用以控制服務(wù)器只啟動一個實例  

  30.  

  31.  privatestaticfinalint srvPort = 12345;     //控制啟動唯一實例的端口號,這個端口如果保存在配置文件中會更靈活  

  32.  

  33.  /**

  34.   * 定時任務(wù)配置文件

  35.   */  

  36.  privatestatic String CONFIG_FILE = "cn/com/pansky/xmdswz/application/scheduler/Scheduling-bean.xml";  

  37.  

  38.  

  39.  public Scheduler() {  

  40.    //檢測系統(tǒng)是否只啟動一個實例  

  41.    checkSingleInstance();  

  42.  

  43.    //下面讀取Spring的配置文件  

  44.    SystemConfig cfg = new SystemConfig();  

  45.    String config = cfg.parseParam("SCHEDULER.CONFIG_FILE", false);  

  46.    if(config!=null && !"".equals( config.trim()))  

  47.      CONFIG_FILE = config;  

  48.    log.debug("CONFIG_FILE: "+CONFIG_FILE);  

  49.  }  

  50.  

  51.  /**

  52.   * 主函數(shù)

  53.   * @param args String[]

  54.   * @throws Exception

  55.   */  

  56.  publicstaticvoid main(String[] args) throws Exception{  

  57.    Scheduler sch = new Scheduler();  

  58.    sch.execute();  

  59.  }  

  60.  

  61.  /**

  62.   * 運行定時任務(wù)

  63.   */  

  64.  publicvoid execute() {  

  65.    ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {CONFIG_FILE});  

  66.    BeanFactory factory = (BeanFactory) appContext;  

  67.  

  68.    /**

  69.     * 裝載任務(wù)調(diào)度

  70.     */  

  71.    StdScheduler scheduler = (StdScheduler) factory.getBean("schedulerFactoryBean");  

  72.    //先暫停所有任務(wù),等待裝載緩存代碼表  

  73.    try {  

  74.      scheduler.pauseAll();  

  75.    } catch (SchedulerException ex) {  

  76.      log.error("",ex);  

  77.    }  

  78.  

  79.    /**

  80.     * 裝載緩存代碼表

  81.     */  

  82.    CachedTableMgr cachedtableMgr = (CachedTableMgr) factory.getBean("cachedTableMgr");  

  83.    try {  

  84.      cachedtableMgr.loadCodeTable();  

  85.    } catch (Exception ex) {  

  86.      log.fatal("Load cached table failed. System will exit.", ex);  

  87.      System.exit(0);  

  88.    }  

  89.  

  90.    //重新恢復(fù)所有任務(wù)  

  91.    try {  

  92.      scheduler.resumeAll();  

  93.    } catch (SchedulerException ex) {  

  94.      log.error("",ex);  

  95.    }  

  96.  }  

  97.  

  98.  /**

  99.   * 檢測系統(tǒng)是否只啟動了一個實例

  100.   */  

  101.  protectedvoid checkSingleInstance() {  

  102.    try {  

  103.      srvSocket = new ServerSocket(srvPort); //啟動一個ServerSocket,用以控制只啟動一個實例  

  104.    } catch (IOException ex) {  

  105.      if(ex.getMessage().indexOf("Address already in use: JVM_Bind")>=0)  

  106.        System.out.println("在一臺主機上同時只能啟動一個進程(Only one instance allowed)。");  

  107.      log.fatal("", ex);  

  108.      System.exit(0);  

  109.    }  

  110.  }  

  111. }  

關(guān)于防止JAVA程序重復(fù)啟動的解決辦法問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI