[轉載]防止JAVA程式重複啟動的一個另類解決辦法
我們專案中有一個後臺任務處理程式,是java開發application,用以處理網站提交的一些批次資料檔案,因為這些資料檔案資料量一般都比較大,所以寫了這個批次處理程式,用以非同步處理這些批次資料檔案。這個程式設計成外掛式的,處理各種不同資料檔案的功能單獨作為一個外掛,然後使用Spring來粘合各個元件,這樣就可以很方便地對該程式進行擴充套件。
今天客戶提出一個要求:需要控制這個程式在同一主機上只能啟動一個例項。
為了實現客戶要求,我首先想到就是在資料庫中建一張表,程式啟動時往該表中寫入一個標誌,等程式結束時再刪除標誌。但這種方式存在一個問題就是,如果程式是非正常停止或被殺程式,那麼這個標誌就不可能被清除,那下一次啟動就會誤判為重複啟動;另外,如果用資料庫來記錄啟動標誌的話,還把該程式跟資料庫緊密耦合起來,感覺很彆扭。
排除了第一種方案之後,我以想到了用檔案來儲存啟動標誌(好象一些大型的程式,諸如weblogic好象就是採用在檔案中記錄啟動標誌方式來控制重複啟動的)。客流量然這種方式不需要與資料庫耦合在一起,但也存在程式異常中止而無法清除啟動標誌的問題,所以這個方案也被槍斃了。
我想到的第三種方案就是在JAVA中呼叫作業系統的檢視系統程式的方式來取得系統程式,然後再檢測系統程式有特殊的程式標誌來判斷是否重複啟動。但這種方式一是看起來很彆扭,再者就是Window和 *nix系統中檢視系統程式的命令不一樣,分成幾種情況來處理,無端地增加了程式的複雜性,也不可取。
能不能在記憶體中記錄一個啟動標誌呢?理論上這應該是不可行的,因為跨JVM來相互操作記憶體資料是不可能。我在網上搜了一下,也沒找到相關的例子。
那能不能佔用一點系統共享資源,來換取我們的目標呢?比較容易想到的系統資源並且不能重複使用的資源就是埠。我嘗試採用如下方案:在程式中指定一個不常用的埠(比如:12345),在程式啟動時,就指定的埠啟動一個ServerSocket,這個Socket只是為了佔用這個埠,不接受任何網路連線。如果試圖啟動第二個例項時,程式在該指定埠啟動ServerSocket時就會拋異常,這時我們就可以認為系統已經啟動過了,然後列印提示並直接退出程式即可。這種方式在理論上分析應該可以的,我開始動手修改程式。程式修改如下:
[@more@]
今天客戶提出一個要求:需要控制這個程式在同一主機上只能啟動一個例項。
為了實現客戶要求,我首先想到就是在資料庫中建一張表,程式啟動時往該表中寫入一個標誌,等程式結束時再刪除標誌。但這種方式存在一個問題就是,如果程式是非正常停止或被殺程式,那麼這個標誌就不可能被清除,那下一次啟動就會誤判為重複啟動;另外,如果用資料庫來記錄啟動標誌的話,還把該程式跟資料庫緊密耦合起來,感覺很彆扭。
排除了第一種方案之後,我以想到了用檔案來儲存啟動標誌(好象一些大型的程式,諸如weblogic好象就是採用在檔案中記錄啟動標誌方式來控制重複啟動的)。客流量然這種方式不需要與資料庫耦合在一起,但也存在程式異常中止而無法清除啟動標誌的問題,所以這個方案也被槍斃了。
我想到的第三種方案就是在JAVA中呼叫作業系統的檢視系統程式的方式來取得系統程式,然後再檢測系統程式有特殊的程式標誌來判斷是否重複啟動。但這種方式一是看起來很彆扭,再者就是Window和 *nix系統中檢視系統程式的命令不一樣,分成幾種情況來處理,無端地增加了程式的複雜性,也不可取。
能不能在記憶體中記錄一個啟動標誌呢?理論上這應該是不可行的,因為跨JVM來相互操作記憶體資料是不可能。我在網上搜了一下,也沒找到相關的例子。
那能不能佔用一點系統共享資源,來換取我們的目標呢?比較容易想到的系統資源並且不能重複使用的資源就是埠。我嘗試採用如下方案:在程式中指定一個不常用的埠(比如:12345),在程式啟動時,就指定的埠啟動一個ServerSocket,這個Socket只是為了佔用這個埠,不接受任何網路連線。如果試圖啟動第二個例項時,程式在該指定埠啟動ServerSocket時就會拋異常,這時我們就可以認為系統已經啟動過了,然後列印提示並直接退出程式即可。這種方式在理論上分析應該可以的,我開始動手修改程式。程式修改如下:
java 程式碼
- package cn.com.pansky.xmdswz.application.scheduler;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.quartz.SchedulerException;
- import org.quartz.impl.StdScheduler;
- import org.springframework.beans.factory.BeanFactory;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import cn.com.pansky.xmdswz.system.cache.CachedTableMgr;
- import cn.com.pansky.xmdswz.system.config.SystemConfig;
- import cn.com.pansky.xmdswz.utility.DateUtil;
- import org.quartz.JobDetail;
- import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
- import java.net.ServerSocket;
- import java.io.*;
- /**
- * Title: XXXXXXX
- * Description: XXXXXXXXXXXX
- * Copyright: Copyright (c) 2006
- * Company:
- *
- * @author Sheng Youfu
- * @version 1.0
- */
- public class Scheduler {
- private static Log log = LogFactory.getLog(Scheduler.class);
- private static ServerSocket srvSocket = null; //服務執行緒,用以控制伺服器只啟動一個例項
- private static final int srvPort = 12345; //控制啟動唯一例項的埠號,這個埠如果儲存在配置檔案中會更靈活
- /**
- * 定時任務配置檔案
- */
- private static String CONFIG_FILE = "cn/com/pansky/xmdswz/application/scheduler/Scheduling-bean.xml";
- public Scheduler() {
- //檢測系統是否只啟動一個例項
- checkSingleInstance();
- //下面讀取Spring的配置檔案
- SystemConfig cfg = new SystemConfig();
- String config = cfg.parseParam("SCHEDULER.CONFIG_FILE", false);
- if(config!=null && !"".equals( config.trim()))
- CONFIG_FILE = config;
- log.debug("CONFIG_FILE: "+CONFIG_FILE);
- }
- /**
- * 主函式
- * @param args String[]
- * @throws Exception
- */
- public static void main(String[] args) throws Exception{
- Scheduler sch = new Scheduler();
- sch.execute();
- }
- /**
- * 執行定時任務
- */
- public void execute() {
- ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {CONFIG_FILE});
- BeanFactory factory = (BeanFactory) appContext;
- /**
- * 裝載任務排程
- */
- StdScheduler scheduler = (StdScheduler) factory.getBean("schedulerFactoryBean");
- //先暫停所有任務,等待裝載快取程式碼表
- try {
- scheduler.pauseAll();
- } catch (SchedulerException ex) {
- log.error("",ex);
- }
- /**
- * 裝載快取程式碼表
- */
- CachedTableMgr cachedtableMgr = (CachedTableMgr) factory.getBean("cachedTableMgr");
- try {
- cachedtableMgr.loadCodeTable();
- } catch (Exception ex) {
- log.fatal("Load cached table failed. System will exit.", ex);
- System.exit(0);
- }
- //重新恢復所有任務
- try {
- scheduler.resumeAll();
- } catch (SchedulerException ex) {
- log.error("",ex);
- }
- }
- /**
- * 檢測系統是否只啟動了一個例項
- */
- protected void checkSingleInstance() {
- try {
- srvSocket = new ServerSocket(srvPort); //啟動一個ServerSocket,用以控制只啟動一個例項
- } catch (IOException ex) {
- if(ex.getMessage().indexOf("Address already in use: JVM_Bind")>=0)
- System.out.println("在一臺主機上同時只能啟動一個程式(Only one instance allowed)。");
- log.fatal("", ex);
- System.exit(0);
- }
- }
- }
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/443058/viewspace-915510/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- jQuery裡的silidetoggle方法不停重複動畫效果的解決辦法jQueryIDE動畫
- 電腦一直自動重啟怎麼辦?電腦一直自動重啟的原因和解決辦法
- 記vscode無法啟動解決辦法VSCode
- DefaultIdentifierGenerator 雪花演算法 生成 重複 id 解決辦法IDE演算法
- 驚喜!一個檔案多個【請求類】的另類寫法
- flutter防止widget rebuild終極解決辦法FlutterRebuild
- WinForm MDIParent如何防止重複開啟ORM
- 解決 "Script Error" 的另類思路Error
- win10重啟一直轉圈怎麼辦 win10重啟一直轉圈的解決方法Win10
- 【Mongodb】mongo複製集只剩一個secondery節點的解決辦法MongoDB
- RCmongodb出現id重複問題的簡單解決辦法jztMongoDB
- Ubuntu 重啟無法進入圖形介面的解決辦法Ubuntu
- Jenkins+tomcat自動釋出的熱部署/重啟及遇到的坑解決辦法JenkinsTomcat熱部署
- Asp.net 無法啟動IIS Express 解決辦法ASP.NETExpress
- win10 1903系統重啟後一直在轉圈無法正常啟動的解決方法Win10
- 無法啟動mysql服務”1067 程式意外終止”解決辦法【簡記】MySql
- centos7網路卡啟動不了的解決辦法CentOS
- PyCharm啟動報錯:Failed to create JVM.解決辦法之一PyCharmAIJVM
- ubuntu安裝向日葵後無法啟動的解決辦法Ubuntu
- win10 sxstrace.exe無法啟動解決辦法_win10系統應用程式無法正常啟動怎麼辦Win10
- win10無法關機一直重啟怎麼辦_win10關機立馬自動重啟解決方法Win10
- win10一直藍屏重啟怎麼辦_win10電腦總是藍屏重啟解決辦法Win10
- 一個Java類的載入Java
- ORA-04031的傻瓜解決辦法(轉)
- 【轉】[Java] 防止併發的多種寫法Java
- MySQL組複製的幾個常見問題以及解決辦法MySql
- 更新macOS Big Sur系統後,Parallels Desktop無法啟動的解決辦法!MacParallel
- 【轉】vue mounted 呼叫兩次的解決辦法Vue
- js 防止重複提交方案JS
- win10快速啟動後重啟怎麼辦_win10開機快速啟動後重啟解決方法Win10
- tomcat無法啟動的解決方法Tomcat
- conda 下載速度慢的解決辦法
- win10應用程式無法正常啟動怎麼解決_win10應用程式無法啟動解決教程Win10
- 集合類不安全及解決辦法
- N1盒子掛載磁碟-解決盒子重啟後無法自動掛載問題
- docker dm_task_run failed 啟動失敗解決辦法DockerAI
- 記一次mysql無法啟動的解決方案MySql
- macOS Catalina 已損壞無法開啟的解決辦法Mac
- IDEA專案突然出現異常無法啟動時的有效解決辦法Idea