锁定老帖子 主题:spring+quartz 动态任务方案
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (14)
|
|
---|---|
作者 | 正文 |
发表时间:2010-07-02
公司目前有这样的需求,结合spring+quartz开发个后台的WEB管理系统,系统主要包括以下功能: 1、动态添加、修改和删除数据源,并加入到spring管理。 2、动态注册、修改和删除任务(需要实现的具体quartz业务类),并加入到quartz管理。 3、提供上传第三方包的功能。(主要考虑实现的业务类,需要引入其他的jar包)。 4、在线日志查询分析。 。。。 后台系统的应用领域: 1、执行多个数据库之间的数据交换服务。 2、架设系统与银行之间的通讯服务。 。。。 以前没搞过这方面应用,比较头疼,经过google、百度,初步方案实现如下: 1、实现个servlet用于启动quartz调度。 程序如下: public class DispatchJobServlet extends HttpServlet { private static final long serialVersionUID = -3920177706344758439L; private ApplicationContext ctx; public DispatchJobServlet() { super(); // 初始化自定义类加载器,主要用于加载第三方包和服务程序。 ServiceStartup manguage = new ServiceStartup(); manguage.startUp(); } /** * 初始化 */ public void init(ServletConfig config) throws ServletException { super.init(config); ctx = WebApplicationContextUtils .getRequiredWebApplicationContext(config.getServletContext()); StartJobService taskStartService = (StartJobService) ctx.getBean("startJobService"); //启用个线程开始任务调度 ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(taskStartService); } } 2、到xml文件查询前台添加的任务队列,加入quartz管理并执行。 @Service public class StartJobService implements Runnable { /** * log4j 记录器 */ private static final Logger log = Logger.getLogger(StartJobService.class); @Resource private SchedulerService schedulerService; public void run() { try { while (true) { List<JobBo> list = DomParser.findAllJobXml("db.service.xml"); Iterator<JobBo> i = list.iterator(); while (i.hasNext()) { JobBo job = i.next(); // 如果任务队列不存在则注册并运行 if (!ServiceManager.queryExistsJob(job.getName())) { try { schedulerService.schedule(job); ServiceManager.getJobHolder().put(job.getName(), job); } catch (Exception e) { log.error("服务【" + job.getName() + "】启动失败!", e); throw new SummerException("服务【" + job.getName() + "】启动失败!"); } } Thread.sleep(3000); } } } catch (SummerException e) { throw e; } catch (Exception e) { log.error("调度任务出现异常!", e); } } } 3、封装SchedulerService实现quartz调度的方法 封装的出来quartz的接口: public interface SchedulerService { /** * 自定义任务对象并启动任务 * * @param job * 任务队列业务对象 */ void schedule(JobBo job); /** * 取得所有调度Triggers * * @return */ List<Map<String, Object>> getQrtzTriggers(); /** * 根据名称和组别暂停Tigger * * @param triggerName * @param group */ void pauseTrigger(String triggerName, String group); /** * 恢复Trigger * * @param triggerName * @param group */ void resumeTrigger(String triggerName, String group); /** * 删除Trigger * * @param triggerName * @param group */ boolean removeTrigdger(String triggerName, String group); } 实现类: public class SchedulerServiceImpl implements SchedulerService { private static final Logger log = LoggerFactory .getLogger(SchedulerServiceImpl.class); private Scheduler scheduler; private JobDetail jobDetail; public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; } public void setJobDetail(JobDetail jobDetail) { this.jobDetail = jobDetail; } /** * 自定义任务对象并启动任务 */ public void schedule(JobBo job) { // trigger分类 String category = job.getCategory(); try { if ("cron".equals(category)) { scheduleCron(job); } else { scheduleSimple(job); } } catch (Exception e) { log.error("任务调度过程中出现异常!"); throw new SummerException(e); } } /** * simple任务触发 * * @param job */ private void scheduleSimple(JobBo job) { String name = getTriggerName(job.getName()); // 实例化SimpleTrigger SimpleTrigger simpleTrigger = new SimpleTrigger(); // 这些值的设置也可以从外面传入,这里采用默放值 simpleTrigger.setJobName(jobDetail.getName()); simpleTrigger.setJobGroup(Scheduler.DEFAULT_GROUP); simpleTrigger.setRepeatInterval(1000L); // 设置名称 simpleTrigger.setName(name); // 设置Trigger分组 String group = job.getGroup(); if (StringUtils.isEmpty(group)) { group = Scheduler.DEFAULT_GROUP; } simpleTrigger.setGroup(group); // 设置开始时间 Timestamp startTime = job.getStartTime(); if (null != startTime) { simpleTrigger.setStartTime(new Date()); } // 设置结束时间 Timestamp endTime = job.getEndTime(); if (null != endTime) { simpleTrigger.setEndTime(endTime); } // 设置执行次数 int repeatCount = job.getRepeatCount(); if (repeatCount > 0) { simpleTrigger.setRepeatCount(repeatCount); } // 设置执行时间间隔 long repeatInterval = job.getRepeatInterval(); if (repeatInterval > 0) { simpleTrigger.setRepeatInterval(repeatInterval * 1000); } try { JobDataMap jobData = new JobDataMap(); jobData.put("name", job.getName()); jobData.put("desc", job.getDesc()); jobDetail.setJobDataMap(jobData); scheduler.addJob(jobDetail, true); scheduler.scheduleJob(simpleTrigger); scheduler.rescheduleJob(simpleTrigger.getName(), simpleTrigger .getGroup(), simpleTrigger); } catch (SchedulerException e) { log.error("任务调度出现异常!"); log.error(LogGenerator.getInstance().generate(e)); throw new SummerException("任务调度出现异常!"); } } /** * cron任务触发 * * @param job */ private void scheduleCron(JobBo job) { String name = getTriggerName(job.getName()); try { JobDataMap jobData = new JobDataMap(); jobData.put("name", job.getName()); jobData.put("desc", job.getDesc()); jobDetail.setJobDataMap(jobData); scheduler.addJob(jobDetail, true); CronTrigger cronTrigger = new CronTrigger(name, job.getGroup(), jobDetail.getName(), Scheduler.DEFAULT_GROUP); cronTrigger.setCronExpression(job.getCronExpression()); scheduler.scheduleJob(cronTrigger); scheduler.rescheduleJob(cronTrigger.getName(), cronTrigger .getGroup(), cronTrigger); } catch (Exception e) { log.error("执行cron触发器出现异常!", e); throw new SummerException("执行cron触发器出现异常!"); } } public void schedule(String name, Date startTime, Date endTime, int repeatCount, long repeatInterval, String group) { if (name == null || name.trim().equals("")) { name = UUID.randomUUID().toString(); } else { // 在名称后添加UUID,保证名称的唯一性 name += "&" + UUID.randomUUID().toString(); } try { scheduler.addJob(jobDetail, true); SimpleTrigger SimpleTrigger = new SimpleTrigger(name, group, jobDetail.getName(), Scheduler.DEFAULT_GROUP, startTime, endTime, repeatCount, repeatInterval); scheduler.scheduleJob(SimpleTrigger); scheduler.rescheduleJob(SimpleTrigger.getName(), SimpleTrigger .getGroup(), SimpleTrigger); } catch (SchedulerException e) { throw new RuntimeException(e); } } public void pauseTrigger(String triggerName, String group) { try { scheduler.pauseTrigger(triggerName, group);// 停止触发器 } catch (SchedulerException e) { throw new SummerException(e); } } public void resumeTrigger(String triggerName, String group) { try { scheduler.resumeTrigger(triggerName, group);// 重启触发器 } catch (SchedulerException e) { log.error("重启触发器失败!"); throw new SummerException(e); } } public boolean removeTrigdger(String triggerName, String group) { try { scheduler.pauseTrigger(triggerName, group);// 停止触发器 return scheduler.unscheduleJob(triggerName, group);// 移除触发器 } catch (SchedulerException e) { throw new SummerException(e); } } private Timestamp parseDate(String time) { try { return Timestamp.valueOf(time); } catch (Exception e) { log.error("日期格式错误{},正确格式为:yyyy-MM-dd HH:mm:ss", time); throw new SummerException(e); } } public List<Map<String, Object>> getQrtzTriggers() { // TODO Auto-generated method stub return null; } /** * 获取trigger名称 * * @param name * @return */ private String getTriggerName(String name) { if (StringUtils.isBlank(StringUtils.trim(name))) { name = UUID.randomUUID().toString(); } else { name = name.substring(name.lastIndexOf(".") + 1); // 在名称后添加UUID,保证名称的唯一性 name += "&" + UUID.randomUUID().toString(); } return StringUtils.trim(name); } } 4、覆盖QuartzJobBean的executeInternal方法,根据“name”名实现任务的动态分配 public class EnhanceQuartzJobBean extends QuartzJobBean { /** * log4j 记录器 */ private static final Logger log = Logger .getLogger(EnhanceQuartzJobBean.class); @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { try { JobDetail t = context.getJobDetail(); JobDataMap map = t.getJobDataMap(); String name = map.getString("name"); String desc = map.getString("desc"); ServiceStartupManguage manguage = new ServiceStartupManguage(); manguage.runService(name, desc); } catch (Exception e) { log.error("执行任务出现异常", e); } } public class ServiceStartup { /** * log4j 记录器 */ private static final Logger log = Logger .getLogger(ServiceStartupManguage.class); public void startUp() { // 启动动态重新加载类的服务 StringBuilder sb = new StringBuilder(1024); sb.append(ServiceManager.getHome() + "work"); String jarPath = ServiceManager.getHome() + "ext"; // 遍历ext文件夹,寻找jar文件 File dir = new File(jarPath); String[] subFiles = dir.list(); for (int i = 0; i < subFiles.length; i++) { File file = new File(jarPath + System.getProperty("file.separator") + subFiles[i]); if (file.isFile() && subFiles[i].endsWith("jar")) { sb.append(File.pathSeparator + jarPath + System.getProperty("file.separator") + subFiles[i]); } } ServiceManager.checker = new ClassModifyChecker(ServiceManager.getHome()); ServiceManager.loader = new ServiceClassLoad(DispatchJobServlet.class .getClassLoader(), (String) sb.toString(), ServiceManager.checker); ServiceManager.classPath = sb.toString(); } /** * 启动后台服务 * * @author 任鹤峰 2009-02-03 * @param name * @param desc * @throws ClassNotFoundException * @throws NoSuchMethodException * @throws InstantiationException * @throws IllegalAccessException * @throws InvocationTargetException */ @SuppressWarnings("unchecked") public void runService(String name, String desc) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { try { Object service; Class cls = null; if (null != ServiceManager.loader) { cls = ServiceManager.getLoader().loadClass(name); } else { cls = Class.forName(name); } Class[] par = null; Object[] obj = null; par = new Class[2]; par[0] = String.class; par[1] = String.class; obj = new Object[2]; obj[0] = name; obj[1] = desc; Constructor ct = cls.getConstructor(par); service = ct.newInstance(obj); Method meth = cls.getMethod("start"); meth.invoke(service); cls = null; } catch (Exception e) { log.error("运行注册服务【" + name + "】出现异常", e); } } } } 5、quartz的配置文件: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="schedulerFactory" singleton="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="applicationContextSchedulerContextKey" value="applicationContextKey" /> <property name="configLocation" value="classpath:quartz.properties" /> </bean> <bean id="jobDetail" singleton="false" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass"> <value> com.xeranx.summer.scheduling.EnhanceQuartzJobBean </value> </property> </bean> <bean id="schedulerService" singleton="false" class="com.xeranx.summer.scheduling.service.SchedulerServiceImpl"> <property name="jobDetail"> <ref bean="jobDetail" /> </property> <property name="scheduler"> <ref bean="schedulerFactory" /> </property> </bean> </beans> 以上是实现的主要代码: 目前可以实现任务的动态添加并执行,现在的问题是添加多个任务时,最后面的任务会覆盖之前所有的任务。 检查了N久未果,还请大家帮忙分析,或者对后台管理系统有更好的需求方案。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-07-02
没人感兴趣,我自己顶下。
|
|
返回顶楼 | |
发表时间:2010-07-02
ibadboy 写道 没人感兴趣,我自己顶下。 quartz+ant+ant-contrib 这才是正确的解决之道,欲活用,单独quartz是要走很多弯路的 |
|
返回顶楼 | |
发表时间:2010-07-03
不是没人感兴趣,而是你自己说话的方式不对.
你第一点做的还行,把目标提出来了.可是你不说自己的想法和办法,直接就是一坨代码... ... |
|
返回顶楼 | |
发表时间:2010-07-03
JE帐号 写道 不是没人感兴趣,而是你自己说话的方式不对.
你第一点做的还行,把目标提出来了.可是你不说自己的想法和办法,直接就是一坨代码... ... 谢谢你的提点,可能我比较仓促没把问题描述清楚。 我一开始的思想,通过web.xml加载启动一个servlet开始整个任务调度,通过前台管理界面把注册的quartz服务添加到一个xml文件中,然后启动的线程解析xml数据,把服务添加到quartz任务队列,循环分别调用封装的 schedulerService.schedule(job)方法启动quartz的trigger。 其中schedule方法中根据Spring注入的jobDetail类,去执行jobclass中定义的EnhanceQuartzJobBean类,该类是我继承自Spring的QuartzJobBean类。 现在问题的关键是,我所有的quartz服务都是通过EnhanceQuartzJobBean类中executeInternal方法调度。在executeInternal方法中我会根据前台注册的“服务名”动态初始化,并执行初始化类的相关的业务。也就是下面这段通过反射执行的代码: <code> public void runService(String name, String desc) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { try { Object service; Class cls = null; if (null != ServiceManager.loader) { cls = ServiceManager.getLoader().loadClass(name); } else { cls = Class.forName(name); } Class[] par = null; Object[] obj = null; par = new Class[2]; par[0] = String.class; par[1] = String.class; obj = new Object[2]; obj[0] = name; obj[1] = desc; Constructor ct = cls.getConstructor(par); service = ct.newInstance(obj); Method meth = cls.getMethod("start"); meth.invoke(service); cls = null; } catch (Exception e) { log.error("运行注册服务【" + name + "】出现异常", e); } } </code> 目前任务都可以跑,只不过多个任务一起跑的话,最后面的会覆盖前面所有的服务,而一直跑下去,前面的服务确已经停止。 我分析下来,估计就是EnhanceQuartzJobBean类的问题,因为我所有的quartz服务都是通过调用该类去执行的。 不知道大家清楚没有,还请赐教,或者有其他的解决方案。 |
|
返回顶楼 | |
发表时间:2010-07-03
楼主公司项目的特性,刚好osgi架构适合你公司的要求.
|
|
返回顶楼 | |
发表时间:2010-07-03
以前我写过一个,其实不难实现!好好想想就行了
|
|
返回顶楼 | |
发表时间:2010-07-04
楼主封装得不错,可以参考下国内开源框架甲壳虫j2ee框架里面对Quartz的封装。
|
|
返回顶楼 | |
发表时间:2010-07-04
最后修改:2010-07-04
很明显,你的jobDetail是通过spring注入进去的,然后你所有的
scheduler.addJob(jobDetail, true); 方法中的jobDetail一直是同一个。。
so....长江后浪推前浪了....
解决办法就是你动态产生一个jobDetail 就OK了
一个jobDetail就是一个任务,一个jobDetail 对应N个 Trigger
|
|
返回顶楼 | |
发表时间:2010-07-04
spring3里面 一个annotation就搞定咯
看看spring reference的例子吧 |
|
返回顶楼 | |