- 浏览: 1609351 次
- 性别:
- 来自: 厦门
文章分类
- 全部博客 (603)
- T_java (145)
- T_script&ASP (51)
- T_C/C++ (25)
- T_PowerBuilder (11)
- T_Database (53)
- T_odoo (7)
- T_应用服务器 (50)
- T_专_条形码 (6)
- T_专_负载均衡器 (4)
- T_操作系统 (94)
- T_信息安全 (41)
- T_专_搜索引擎 (14)
- T_L_PHP (58)
- T_L_Delphi (18)
- T_L_.NET、C#、VisualStudio (25)
- T_L_Objective-C (6)
- T_移动开发 (53)
- T_网络 (109)
- T_大数据 (2)
- T_嵌入式 (2)
- T_小众技术 (24)
- T_未分类 (58)
- L_旅游印记 (1)
- L_生活随笔 (48)
- L_中国文化 (18)
- L_户外与生存 (0)
最新评论
-
csbean4004:
不知道哪传来得恶习,发帖子不好好发,故意弄错一些东西,很讨厌
让HTML5支持后置摄像头 -
withthewind:
终于找到一个可以用的了。。。
如何用VBA取得Word文档中的标题前面的序号 -
busbby:
兄弟,无法下载,说文件不完整
一个好用的Outlook ost格式文件转pst文件的工具 -
yijavakevin:
密码啊~解压密码多少?
一个二维条形码组件 -
vipbooks:
你给的那个链接根本无法下载,跳到官网看了下最新版12M,但点下 ...
十步以内完成精细web打印
我们项目中有一个后台任务处理程序,是java开发application,用以处理网站提交的一些批量数据文件,因为这些数据文件数据量一般都比较大,所以写了这个批量处理程序,用以异步处理这些批量数据文件。这个程序设计成插件式的,处理各种不同数据文件的功能单独作为一个插件,然后使用Spring来粘合各个组件,这样就可以很方便地对该程序进行扩展。
今天客户提出一个要求:需要控制这个程序在同一主机上只能启动一个实例。
为了实现客户要求,我首先想到就是在数据库中建一张表,程序启动时往该表中写入一个标志,等程序结束时再删除标志。但这种方式存在一个问题就是,如果程序是非正常停止或被杀进程,那么这个标志就不可能被清除,那下一次启动就会误判为重复启动;另外,如果用数据库来记录启动标志的话,还把该程序跟数据库紧密耦合起来,感觉很别扭。
排除了第一种方案之后,我以想到了用文件来保存启动标志(好象一些大型的程序,诸如weblogic好象就是采用在文件中记录启动标志方式来控制重复启动的)。客流量然这种方式不需要与数据库耦合在一起,但也存在程序异常中止而无法清除启动标志的问题,所以这个方案也被枪毙了。
我想到的第三种方案就是在JAVA中调用操作系统的查看系统进程的方式来取得系统进程,然后再检测系统进程有特殊的进程标志来判断是否重复启动。但这种方式一是看起来很别扭,再者就是Window和 *nix系统中查看系统进程的命令不一样,分成几种情况来处理,无端地增加了程序的复杂性,也不可取。
能不能在内存中记录一个启动标志呢?理论上这应该是不可行的,因为跨JVM来相互操作内存数据是不可能。我在网上搜了一下,也没找到相关的例子。
那能不能占用一点系统共享资源,来换取我们的目标呢?比较容易想到的系统资源并且不能重复使用的资源就是端口。我尝试采用如下方案:在程序中指定一个不常用的端口(比如:12345),在程序启动时,就指定的端口启动一个ServerSocket,这个Socket只是为了占用这个端口,不接受任何网络连接。如果试图启动第二个实例时,程序在该指定端口启动ServerSocket时就会抛异常,这时我们就可以认为系统已经启动过了,然后打印提示并直接退出程序即可。这种方式在理论上分析应该可以的,我开始动手修改程序。程序修改如下:
- 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);
- }
- }
- }
经过测试,程序能很好地满足我们的要求,问题解决。
我之所以称这种方式另类,是因为这种方式以牺牲一个端口的代价来达到我们的设计要求,采用这种方式的人应该不多。但我认为,只要我们牺牲的代价与我们的目标比较起来是在可接受的范围内,这种方式就是可取的,这与我们花钱增加内存来让程序运行更快在本质应该是相同的。
评论
http://www.mxjava.com/blog/article.asp?id=3
而且最近公司的项目中也用了这个思路.呵呵~~我们都在杭州哦!
看了你写的方法,但我有一点疑问,你在选择服务器端口时,采用的是自动增加的方法,这个方法没问题,但你在console类中,iPort = 50000。由于在不同的jvm下运行,这个Consonle客户端每次连接的都是端口50000,那么,Server中端口增加非但没有意义,反而会引起误操作。假如端口50000被其它程序占用,那么该程序还不能很好做到防止重复运行了。我想还是指定端口吧。望指教。
我想你没有搞懂这个程序是做什么的。建议再认真看一遍。
首选nio锁文件
次选socket端口
已经是公认的解决方案了
if(instance != null) return "在一台主机上同时只能启动一个进程(Only one instance allowed)。";
定时记录访问时间到文件,
超时即为无程序运行或程序导常。
这样不是更容易些?
http://www.mxjava.com/blog/article.asp?id=3
而且最近公司的项目中也用了这个思路.呵呵~~我们都在杭州哦!
看了你写的方法,但我有一点疑问,你在选择服务器端口时,采用的是自动增加的方法,这个方法没问题,但你在console类中,iPort = 50000。由于在不同的jvm下运行,这个Consonle客户端每次连接的都是端口50000,那么,Server中端口增加非但没有意义,反而会引起误操作。假如端口50000被其它程序占用,那么该程序还不能很好做到防止重复运行了。我想还是指定端口吧。望指教。
Nirvana 1 小时前
我觉得利用文件来保存启动标记也可以,只是思路变换一下,打开后一个实例,强制关闭前一个:
启动实例时生成唯一标记,保存变量,写入文件,然后循环读取文件内容,判断是否与保存变量相等,不等则退出。
第二实例运行时因操作同一文件,必然会满足第一实例不等的条件,迫使第一实例关闭。
刚才做了个小实验,发现也可行。
我个人觉得Nirvana的方法不是很可行,你说在第二实例启动时,强制关闭第一实例,这存在两个问题:
1、如何做到从第二实例中关闭第一实例?
2、即使可以关闭第一实例,那第一实例中正在处理的数据咋办?而且这样强制关闭第一实例,感觉有点象病毒噢,呵呵!
我也倾向于通过锁定文件来保证java启动程序的唯一性,以前我写过一个这样的mini程序库。
关闭第一个实例是通过在程序中添加JMX接口,允许远程调用该接口来关闭程序,而不是执行kill -9这样的操作
来关闭。
巧了.这个思路我N年前也用过.
http://www.mxjava.com/blog/article.asp?id=3
而且最近公司的项目中也用了这个思路.呵呵~~我们都在杭州哦!
hiswing:呵呵,与我心有戚戚焉,握个手先!
http://www.mxjava.com/blog/article.asp?id=3
而且最近公司的项目中也用了这个思路.呵呵~~我们都在杭州哦!
Nirvana 1 小时前
我觉得利用文件来保存启动标记也可以,只是思路变换一下,打开后一个实例,强制关闭前一个:
启动实例时生成唯一标记,保存变量,写入文件,然后循环读取文件内容,判断是否与保存变量相等,不等则退出。
第二实例运行时因操作同一文件,必然会满足第一实例不等的条件,迫使第一实例关闭。
刚才做了个小实验,发现也可行。
我个人觉得Nirvana的方法不是很可行,你说在第二实例启动时,强制关闭第一实例,这存在两个问题:
1、如何做到从第二实例中关闭第一实例?
2、即使可以关闭第一实例,那第一实例中正在处理的数据咋办?而且这样强制关闭第一实例,感觉有点象病毒噢,呵呵!
1.如果觉得不清楚,补充一下:第二个实例运行时,改变了同一文件所记录的启动标记,而第一个实例始终循环读取文件进行比较,此时标记变量值与文件内容不等即关闭。
这个应该很简单的。
2.请问你非正常情况下退出时,数据是怎么处理的?难道都是随它去吗?
另外,我的这关闭,应该是正常退出,尚在程序可控制范围下。
扯病毒就太远了,倒是服务器端口经常被封的说。
SingleApplicationLock.java
有个实现了。
似乎有关,接着试
还有Java Logging API也会产生一个.lck文件,
可不可以通过标志文件的存取权限来判断?
试试看去,10分钟后回来。
Nirvana 1 小时前
我觉得利用文件来保存启动标记也可以,只是思路变换一下,打开后一个实例,强制关闭前一个:
启动实例时生成唯一标记,保存变量,写入文件,然后循环读取文件内容,判断是否与保存变量相等,不等则退出。
第二实例运行时因操作同一文件,必然会满足第一实例不等的条件,迫使第一实例关闭。
刚才做了个小实验,发现也可行。
我个人觉得Nirvana的方法不是很可行,你说在第二实例启动时,强制关闭第一实例,这存在两个问题:
1、如何做到从第二实例中关闭第一实例?
2、即使可以关闭第一实例,那第一实例中正在处理的数据咋办?而且这样强制关闭第一实例,感觉有点象病毒噢,呵呵!
启动实例时生成唯一标记,保存变量,写入文件,然后循环读取文件内容,判断是否与保存变量相等,不等则退出。
第二实例运行时因操作同一文件,必然会满足第一实例不等的条件,迫使第一实例关闭。
刚才做了个小实验,发现也可行。
complystill 20 分钟前
确实是很不错的一个方法, 绝大部分服务器系统上, 端口相对来说还是比较cheap的.
这个问题经典的跨平台解决方法是建一个命名的系统互斥量, 它的生命周期也是跟着进程的. 不过Java平台不倾向于提供直接操作宿主系统资源的途径, 自己也是以虚拟机为全部逻辑环境, 不提供宿主系统范围的Inter-JVM-Communication机制. 用端口绑定方式来实现互斥确实有点另类, 不过对于纯Java应用来说不失为最佳的解决方案.
另可以有一点改进的地方, 就是绑定到 InetAddress.getLocalHost() 这个本机地址(在大多数OS上相当于127.0.0.1), 这样进一步不会占用服务器外部ip上的端口. 不过一般的服务器上服务进程通常也不指定具体外部地址, 而是绑定到所有本机地址的端口, 即便这样改过以后还是会影响他们的启动. 所以这个改进除非在很大型的系统上, 服务应用各自指定具体的外部地址去绑定时才有作用.
谢谢各位的回复,特别是complystill和Sunteya 给我开扩了思路。
我得到的回报比我给出的多得多啊!!
发表评论
-
SpringBoot Fat Jar解压运行
2018-06-28 21:40 2258SpringBoot已经成为当前最流行的微服务 ... -
一句话实现五星评分显示
2018-06-05 08:31 998Python: rate = 1 #rate 取值 ... -
来算google的可视化编程工具——Blockly,不仅仅是玩具
2017-10-16 21:34 33122Blockly - 来自Google的可 ... -
安卓动态分析工具 Inspeckage
2017-08-07 08:46 0工具介绍 一个基于Xposed 开发的应用动态分析工具 g ... -
Android逆向之旅---静态方式破解微信获取聊天记录和通讯录信息
2017-08-07 08:37 0一、猜想数据存放路径 微信现在是老少皆宜,大街小巷都在使用 ... -
破解微信数据库 并查询数据上传服务器
2017-08-07 08:29 0由于工作需求破解了微信的数据库 并获取想要的信息上传服 ... -
安卓黑科技之HOOK详解
2017-08-07 08:21 0本文带大家进入到安卓另一个世界 互联网攻防大战 Xpos ... -
安卓逆向之基于Xposed-ZjDroid脱壳
2017-08-07 08:18 0前言 之前介绍了普通常见的反编译模式 但对于使用了 360 ... -
十步以内完成精细web打印
2017-06-21 11:44 7369注意: 康虎云报表组 ... -
浏览器端精准打印或套打组件
2017-01-18 13:05 6698注意: 康虎云报表 ... -
疯狂软件对Oracle放弃Java EE的看法
2016-08-14 22:38 526来源:http://javaligang ... -
几个Java相关的思维导图
2016-03-17 13:07 955来源:http://blog.csdn.net/jackf ... -
jasperReport Applet 打印
2016-02-01 16:33 870Applet方式的原理是本地下载Applet以及Jas ... -
为Java说句公道话
2016-01-24 10:59 713为Java说句公道话 有些 ... -
Mybatis Generator配置详解(中文)_转
2015-12-17 16:44 920来自: http://www.jianshu.com/p/e ... -
一个提供大量数据模型的网站
2015-12-17 14:00 983网站地址是:http://www.databaseansw ... -
采用ajp代理模式配置Apache+tomcat实现负载均衡(转)
2015-11-13 10:22 870这一种方法,配置简单,性能也高。附AJP介绍: AJP ... -
MyBatis配置文件修改侦测及重载的实现
2015-07-31 13:53 2334MyBatis配置文件修改侦测及重载的实现: /** ... -
Spring optional @PathVariable?
2015-07-09 13:13 915Q: Is it possible to somehow ... -
The forked VM terminated without saying properly goodbye. VM crash or System.exi
2015-07-07 18:22 4299The forked VM terminated witho ...
相关推荐
服务器端避免表单的重复提交,利用同步令牌来解决重复提交的基本原理如下:(1)用户访问提交数据的页面,服务器端在这次会话中,创建一个session对象,并产生一个令牌值,将这个令牌值作为隐藏输入域的值,随表单一起发送到...
继承允许一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码复用。多态则使得子类对象可以替换父类对象,提高了程序的灵活性。 在雍俊海的教程中,可能会涉及异常处理,这是Java中处理错误的一种机制。...
`表示声明了一个整型变量age并赋值为25。 2. **数据类型**:Java有两大类数据类型:基本数据类型(如int、char、boolean)和引用数据类型(如类、接口、数组)。理解不同类型的特点和使用场景是编写有效Java代码的...
windows下启动java jar包的bat脚本 javaw后台启动,jdk1.8或者更高,winserver 2016 经过测试
总之,"一些比较有意思的Java小程序"是一个很好的学习资源,它提供了实践和探索Java编程的多种途径。对于那些想要踏入Java世界的新手来说,这是一个很好的起点,也是不断提升编程技能的良好平台。
在Java编程领域,创建一个生日祝福程序是一种常见的实践,它能为用户提供个性化和有趣的互动体验。这个名为"生日祝福java程序代码"的压缩包显然包含了多个功能模块,旨在为生日庆祝活动增添乐趣。以下是该程序可能...
解决双击 jar 和 class文件不能直接在windows命令提示符下运行的问题,不用手动修改注册表和环境变量,把文件的打开方式设为本程序即可
标题《Java Web程序设计教程》与描述《Java Web程序设计教程 Java Web程序设计教程》中的知识点主要涵盖了Java Web应用开发领域的核心技术与实践。本书作为21世纪高等学校计算机规划教材,由范立锋与林果园共同编著...
本文主要探讨如何使用InstallShield工具来创建一个针对Java Web程序的.exe安装程序,该程序不仅包含了JDK(Java Development Kit),还集成了Resin服务器。 InstallShield是一款功能强大的安装制作软件,它允许...
为了方便地启动Java应用程序(通常是以`.jar`文件的形式存在),我们可以创建一个批处理文件(`.bat`文件)。这个文件会调用Java运行时环境(JRE或JDK)来执行指定的`.jar`文件。此外,我们还将介绍如何通过参数设置...
《Java程序设计百事通》是由知名IT教育专家张洪斌编著的一本全面解析Java编程的指南。这本书深入浅出地介绍了Java语言的核心概念、语法特性以及实际应用,旨在帮助初学者快速掌握Java编程技能,并为有经验的开发者...
"200道Java程序设计练习题 PDF" 是一个宝贵的资源,旨在帮助学习者从初级到高级逐步提升Java编程技能。这份资料不仅适合初学者,也对有经验的Java开发者具有很高的参考价值,尤其是对于准备Java面试的人来说,它能够...
这个“java做的抽奖小程序”就是这样一个实用工具,它利用Java编程语言实现,为用户提供了简单易用的抽奖功能。源码的提供不仅能让开发者深入理解抽奖程序的工作原理,还能够作为学习和参考的实例。 首先,我们来看...
总的来说,这个Java桌面提醒程序项目结合了Java编程、XML数据存储和GUI设计等多个方面的技术,为用户提供了一个便捷的个人事务管理工具。通过深入理解这些技术并进行实践,开发者不仅可以提升编程技能,还能掌握如何...
这个"164个完整的Java源程序代码"集合提供了一个丰富的学习资源,涵盖了多种Java编程概念和技术。通过研究这些源代码,我们可以深入理解Java编程的核心原理,并提升实际编程技能。 首先,这些Java源代码可能包括了...
总的来说,这个合集提供了一个全面的Java学习平台,涵盖了基础语法、图形用户界面设计、算法实现、游戏开发、网络编程等多个方面。对于想要深入理解和实践Java的开发者来说,这是一个不可多得的学习资源。通过分析和...
1. **OPC (OLE for Process Control)**:这是最常用的方式,它提供了一个标准接口,使得Java可以通过OPC服务器与PLC进行数据交换。Java中有很多开源的OPC客户端库,如J-OPC和Kepware等,可以方便地集成到项目中。 2...
本资源提供了一个基于Java实现的网络蜘蛛程序及其源码,适用于学习和研究网络爬虫技术。这个程序依赖于Apache Commons HttpClient v3.0库,这是一个强大的HTTP客户端库,为Java开发者提供了丰富的功能来执行HTTP请求...
java与c#MD5加密方法得到不一致解决办法,MD5加密后得到不一致结果
*计算24点 难点是递归算法归纳(双重for循环加递归啊 容易蒙) 以及加括号(完美... *程序逻辑竭力去除重复解(加法乘法交换律 不算重复) 但无法完全 最后只能以Set 解决问题 做了两天 接近完美 差在效率 10分 不喜勿下