ICE.ICE|

韩憨

园龄:4年7个月粉丝:42关注:47

防止JAVA程序重复启动的一个另类解决办法

http://blog.itpub.net/443058/viewspace-915510/

我们项目中有一个后台任务处理程序,是java开发application,用以处理网站提交的一些批量数据文件,因为这些数据文件数据量一般都比较大,所以写了这个批量处理程序,用以异步处理这些批量数据文件。这个程序设计成插件式的,处理各种不同数据文件的功能单独作为一个插件,然后使用Spring来粘合各个组件,这样就可以很方便地对该程序进行扩展。
今天客户提出一个要求:需要控制这个程序在同一主机上只能启动一个实例。
为了实现客户要求,我首先想到就是在数据库中建一张表,程序启动时往该表中写入一个标志,等程序结束时再删除标志。但这种方式存在一个问题就是,如果程序是非正常停止或被杀进程,那么这个标志就不可能被清除,那下一次启动就会误判为重复启动;另外,如果用数据库来记录启动标志的话,还把该程序跟数据库紧密耦合起来,感觉很别扭。
排除了第一种方案之后,我以想到了用文件来保存启动标志(好象一些大型的程序,诸如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: www.pansky.com.cn
*
* @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);
}
}
}
复制代码

本文作者:韩憨

本文链接:https://www.cnblogs.com/hanby/p/16650671.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   韩憨  阅读(388)  评论(0编辑  收藏  举报
//看板娘

哥伦布

评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 隔离 (Studio Live Duet) 陈凯咏,林家谦
  2. 2 明知做戏 吴雨霏
  3. 3 残酷游戏 卫兰
  4. 4 你,好不好? 周兴哲
  5. 5 我可以 蔡旻佑
  6. 6 云烟成雨 房东的猫
  7. 7 说散就散 JC 陈咏桐
  8. 8 我配不上你 夏天Alex
  9. 9 不再联系 夏天Alex
  10. 10 等我先说 夏天Alex
  11. 11 我知道他爱你 夏天Alex
  12. 12 多想在平庸的生活拥抱你 隔壁老樊
  13. 13 这一生关于你的风景 隔壁老樊
  14. 14 我曾 隔壁老樊
  15. 15 关于孤独我想说的话 隔壁老樊
  16. 16 过客 周思涵
  17. 17 备爱 周思涵
  18. 18 嚣张 en
  19. 19 海口 后弦
明知做戏 - 吴雨霏
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : Xia Zhi

作曲 : Fong Man Leung

编曲 : 吴国恩

监制 : Gary Chan

等你的汽水喝一半给你加片薄冰

等你的桌面满泻我总会打理重整

不想纯情 不够聪明

你未发现我的身影

得我帮你依照编码整理家里电影

得我帮你依照编码整理家里电影

只会得我一个帮你选购喜爱铃声

天天如常 估你心情

等一个眼神求证 一闪擦过如流星

怎么我为我做过的感到惊怕

就像爱吗我也不肯定恐怕

我以为存在吗 千变万化

从来不肯开口可相信吗 离谱吗

请你不要阻我喜欢你

明明是爱但你未说话你扮作闪避

这个沉默冷静的你毫无办法处理

其实我亦怕是错摸心理

总有天会等到好天气

游行示爱大叫着你在某大片草地

等你无用退避不过仍然害羞的你

还是顾忌太不争气 明知做戏

即使你未太在意不感到惊讶

即使你未太在意不感到惊讶

现在要说爱你请准备招架

勇气还存在吗 不要害怕

随时真的胆敢亲手送花 离谱吗

请你不要阻我喜欢你

明明是爱但你未说话你扮作闪避

这个沉默冷静的你亳无办法处理

其实我亦怕是错摸心理

总有天会等到好天气

游行示爱大叫着你在某大片草地

等你无用退避不过仍然害羞的你

还是顾忌太不争气 明知做戏

不过不要阻我紧张你

如何令你愉快让我办妥为你准备

喜爱沉默冷静的你还是自信的你

仍愿意为你造一些惊喜

总有天会等到好天气

游行示爱大叫着你在某大片草地

等你无用退避不过途人目光不理

期待贴着你的手臂 无须做戏

等你喜爱等你不爱就凭摘毫验证

等你喜爱等你不爱就凭摘毫验证

想爱不爱偏爱不理亦同样难划清

天天如常 估你心情

不想扑索来求证 争取过趁还年青

终于你下决定来答应 太动听

点击右上角即可分享
微信分享提示