再谈quartz

本文探讨了Quartz定时任务在Tomcat环境下遇到的问题及解决方案,包括因连接池导致的JobPersistenceException异常处理方法,以及如何避免ObjectAlreadyExistsException异常导致的调度器失效。

quartz集成到tomcat采用是使用 ServletContextListener方案,参见http://www.blogjava.net/Unmi/archive/2008/05/01/197657.html

tomcat运行一段时间后,控制台报以下错误
2009-03-06 09:38:21 [org.quartz.core.ErrorLogger]-[ERROR] An error occured while scanning for the next trigger to fire.
org.quartz.JobPersistenceException: Couldn't rollback jdbc connection. Io 异常: Connection reset by peer: socket write error [See nested exception: java.sql.SQLException: Io 异常: Connection reset by peer: socket write error]
at org.quartz.impl.jdbcjobstore.JobStoreSupport.rollbackConnection(JobStoreSupport.java:2319)
at org.quartz.impl.jdbcjobstore.JobStoreTX.acquireNextTrigger(JobStoreTX.java:1222)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:233)
* Nested Exception (Underlying Cause) ---------------
java.sql.SQLException: Io 异常: Connection reset by peer: socket write error
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:134)
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:179)
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:333)
at oracle.jdbc.driver.OracleConnection.rollback(OracleConnection.java:1380)
at org.apache.commons.dbcp.DelegatingConnection.rollback(DelegatingConnection.java:328)
at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.rollback(PoolingDataSource.java:312)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.rollbackConnection(JobStoreSupport.java:2317)
at org.quartz.impl.jdbcjobstore.JobStoreTX.acquireNextTrigger(JobStoreTX.java:1222)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:233)

附开发环境:
winxp+eclipse3.2.2+jdk5.0+tomcat5.5.20+quartz1.5.2+oracle 11g


解决方案参考采用http://www.javauu.com/thread-16-1-1.html

具体描述如下:

      一直以来都相信从连接池获取过来的连接对象应该都是打开的,不可能引起的这个问题;
后面经过调试发现,在抛出异常之前Connection对象都不是null的,只是因为其状态已经被close了.
现在可以证实一点,从连接池里面获取过来的Connection并非全部是可用,如果你仔细去了解其他高级选项的配置,就会体会得到这一点.

     由于quartz默认采用连接池是dbcp,而当使用DBCP时,数据库连接因为某种原因断掉后,再从连接池中取得连接又不进行验证,这时取得的连接实际已经时无效的数据库连接了

解决方法:
    方法一:设置weblogic上面的连接池参数,打开要配置的连接池的配置页面,点击Connection选项卡,在下面找到一个show,点击,就会显示连接池的高级配置选项,
勾选"Test Reserver Connetion",勾选这个的意思,就是要让weblogic在向客户端提供Connection之前,都先自己测试一下这个Connection是 否可用,防止weblogic向客户端发送已经关闭的Connection对象,导致应用程序抛出异常.
    方法二:当然还有另一种方法就是在程序这边作相应的判断,每从数据连接池中获取一个Connection对象时,都要先判断其他状态是否是Open的,如果不是Open的,就重新到池里面再获取一个新的连接,直到获取的连接是可用的为止。

while(conn.isClosed()){
       conn=getConnectionFromPool();
}

在配置连接池时,建议大家多了解其他里面的各个参数项,

 

 

补记:解决ObjectAlreadyExistsException异常导致调度器失效

2009-03-25 16:12:05 [vs.dascsl.dbaccess.datatransfer.ScheduleStartListener]-[ERROR] Quartz failed to initialize
org.quartz.ObjectAlreadyExistsException: Unable to store Job with name: 'b8d44040-f803-443c-8ce8-4b10738a579e' and group: 'DEFAULT', because one already exists with this identification.
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1098)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$3.execute(JobStoreSupport.java:1047)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$40.execute(JobStoreSupport.java:3688)

 

//在ScheduleStartListener加载所有定时器

for ( ....){

 

.....//代码略


通过try{


...
//代码略


} catch    (ObjectAlreadyExistsException e){//已经解决


continue;//捕获ObjectAlreadyExistsException后直接加载下一个定时器,否则调度器失效


}


.....//代码略

}

Quartz 是一个功能强大的任务调度框架,广泛应用于企业级应用中,用于处理复杂的任务调度需求。以下是关于 Quartz 的使用指南、配置方法以及问题解决的详细说明。 ### 使用指南 Quartz 提供了多种方式来定义和管理任务。核心组件包括 `Job`(任务)、`Trigger`(触发器)和 `Scheduler`(调度器)。任务的执行逻辑通常通过实现 `Job` 接口来定义,而触发器则决定了任务何时执行。调度器负责管理任务和触发器的生命周期。 以下是一个简单的任务实现示例: ```java import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class SimpleJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("任务正在执行..."); } } ``` 接下来,需要定义一个触发器来指定任务的执行时间。可以使用 `CronTrigger` 来基于 CRON 表达式进行调度: ```java import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; public class QuartzExample { public static void main(String[] args) throws Exception { // 创建任务详情 JobDetail job = JobBuilder.newJob(SimpleJob.class) .withIdentity("simpleJob", "group1") .build(); // 创建触发器,每分钟执行一次 CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity("cronTrigger", "group1") .withSchedule(CronScheduleBuilder.cronSchedule("0 * * * * ?")) .build(); // 获取调度器并启动 SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.start(); // 将任务和触发器注册到调度器 scheduler.scheduleJob(job, trigger); } } ``` ### 配置方法 Quartz 提供了丰富的配置选项,以支持持久化、集群部署和高可用性等高级特性。这些配置通常在 `quartz.properties` 文件中进行设置。 #### 持久化配置 为了确保任务调度的状态在系统重启后仍然保留,可以将 Quartz 配置为使用数据库进行持久化。以下是一个简单的数据库配置示例: ```properties org.quartz.scheduler.instanceName=MyScheduler org.quartz.threadPool.threadCount=3 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.dataSource=myDS org.quartz.dataSource.myDS.driver=com.mysql.cj.jdbc.Driver org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3306/quartz org.quartz.dataSource.myDS.user=root org.quartz.dataSource.myDS.password=root org.quartz.dataSource.myDS.maxConnections=5 ``` #### 集群配置 在集群环境中,多个 Quartz 实例可以共享同一个数据库,以实现任务的高可用性和负载均衡。以下是一个集群配置的示例: ```properties org.quartz.scheduler.instanceName=MyClusteredScheduler org.quartz.scheduler.instanceId=AUTO org.quartz.threadPool.threadCount=3 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.dataSource=myDS org.quartz.jobStore.isClustered=true org.quartz.jobStore.clusterCheckinInterval=20000 org.quartz.dataSource.myDS.driver=com.mysql.cj.jdbc.Driver org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3306/quartz org.quartz.dataSource.myDS.user=root org.quartz.dataSource.myDS.password=root org.quartz.dataSource.myDS.maxConnections=5 ``` ### 问题解决 在使用 Quartz 时,可能会遇到一些常见问题。以下是一些解决方案和最佳实践: 1. **任务未按预期执行**:检查 CRON 表达式是否正确,并确保调度器已启动。可以通过日志查看调度器的状态和任务的执行情况。 2. **持久化失败**:确保数据库连接配置正确,并且数据库表结构符合 Quartz 的要求。可以使用 Quartz 提供的 SQL 脚本来创建所需的表。 3. **集群节点无法同步**:检查集群节点之间的网络连接,并确保所有节点使用相同的数据库配置。此外,确认 `clusterCheckinInterval` 设置合理,以便节点能够及时更新状态。 4. **任务执行异常**:可以通过实现 `SchedulerListener` 接口来监听任务和触发器的状态变化,并在发生错误时进行处理。以下是一个简单的 `SchedulerListener` 实现示例: ```java import org.quartz.SchedulerListener; import org.quartz.SchedulerException; public class MySchedulerListener implements SchedulerListener { @Override public void jobScheduled(Trigger trigger) { System.out.println("任务已调度: " + trigger.getKey()); } @Override public void jobUnscheduled(String triggerName, String triggerGroup) { System.out.println("任务已取消: " + triggerName + "/" + triggerGroup); } @Override public void triggerFinalized(Trigger trigger) { System.out.println("触发器已最终化: " + trigger.getKey()); } @Override public void triggersPaused(String triggerName, String triggerGroup) { System.out.println("触发器已暂停: " + triggerName + "/" + triggerGroup); } @Override public void triggersResumed(String triggerName, String triggerGroup) { System.out.println("触发器已恢复: " + triggerName + "/" + triggerGroup); } @Override public void jobsPaused(String jobName, String jobGroup) { System.out.println("任务已暂停: " + jobName + "/" + jobGroup); } @Override public void jobsResumed(String jobName, String jobGroup) { System.out.println("任务已恢复: " + jobName + "/" + jobGroup); } @Override public void schedulerError(String msg, SchedulerException cause) { System.out.println("调度器错误: " + msg); cause.printStackTrace(); } @Override public void schedulerStarted() { System.out.println("调度器已启动"); } @Override public void schedulerInStandbyMode() { System.out.println("调度器进入待机模式"); } @Override public void schedulerShutdown() { System.out.println("调度器已关闭"); } @Override public void schedulingDataCleared() { System.out.println("调度数据已清除"); } } ``` 通过以上指南和配置方法,可以有效地使用 Quartz 进行任务调度,并解决可能出现的问题。结合实际业务需求,合理配置持久化和集群选项,并应用安全机制,可以确保任务调度系统的安全、稳定和可靠 [^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值