无法检索作业,因为未找到所需的 class,即使之前的触发器已成功触发

Couldn't retrieve job because a required class was not found, even though previous triggers fired successfully

我有一个用 Quartz 定义的 CRON 触发器,它成功触发了几次并在一些循环后以错误状态结束,并显示以下消息(class 名称和包名称已被编辑):

org.quartz.JobPersistenceException: Couldn't retrieve job because a required class was not found: xxx.xxx.xxx.MyQuartzJob
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveJob(JobStoreSupport.java:1393) [quartz-2.3.2.jar!/:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.acquireNextTrigger(JobStoreSupport.java:2864) [quartz-2.3.2.jar!/:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.execute(JobStoreSupport.java:2805) [quartz-2.3.2.jar!/:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.execute(JobStoreSupport.java:2803) [quartz-2.3.2.jar!/:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3864) [quartz-2.3.2.jar!/:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.acquireNextTriggers(JobStoreSupport.java:2802) [quartz-2.3.2.jar!/:na]
    at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:287) [quartz-2.3.2.jar!/:na]
Caused by: java.lang.ClassNotFoundException: xxx.xxx.xxx.MyQuartzJob
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_302]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[na:1.8.0_302]
    at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:151) ~[app.jar:2.4.0-SNAPSHOT]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[na:1.8.0_302]
    at java.lang.Class.forName0(Native Method) ~[na:1.8.0_302]
    at java.lang.Class.forName(Class.java:348) ~[na:1.8.0_302]
    at org.springframework.util.ClassUtils.forName(ClassUtils.java:284) ~[spring-core-5.2.7.RELEASE.jar!/:5.2.7.RELEASE]
    at org.springframework.scheduling.quartz.ResourceLoaderClassLoadHelper.loadClass(ResourceLoaderClassLoadHelper.java:81) ~[spring-context-support-5.2.7.RELEASE.jar!/:5.2.7.RELEASE]
    at org.springframework.scheduling.quartz.ResourceLoaderClassLoadHelper.loadClass(ResourceLoaderClassLoadHelper.java:87) ~[spring-context-support-5.2.7.RELEASE.jar!/:5.2.7.RELEASE]
    at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.selectJobDetail(StdJDBCDelegate.java:852) ~[quartz-2.3.2.jar!/:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveJob(JobStoreSupport.java:1390) [quartz-2.3.2.jar!/:na]

一旦发生此错误,触发器将自身更新为 ERROR 状态并且不会再触发。奇怪的是,触发器已经成功触发了几次(有时多达 4 次),但在下一次迭代时突然无法加载 class。 如果我再次手动将其状态更新为 WAITING 它会在更新后立即触发一次,并恢复其计划:它工作了几个周期,并且在某些时候无法再次启动,并出现我在上面复制的错误,并更新本身到 ERROR 状态。

我不知道为什么,也不知道如何解决这个问题。 Quartz 数据库没有并发访问,因为我们是 运行 单个服务器实例,所以我不明白为什么 class 会被成功找到并加载几次然后却没有,在已部署服务器的相同版本。

完全限定 class数据库中的名称是正确的(包名称 + class 名称)。

如有任何建议,我们将不胜感激。如果需要,请随时询问更多详细信息。

这是 Spring 引导应用程序中遇到的一个非常常见的问题,其中 Spring Quartz 调度程序工厂创建一个默认使用 org.springframework.scheduling.quartz.ResourceLoaderClassLoadHelper 作为 class 加载助手。这个 class 加载助手可以在你的堆栈跟踪中看到。 Quartz 使用 class 加载助手来加载作业实现 classes.

要解决此问题,请将以下 属性 添加到您的 quartz.properties 中:

org.quartz.scheduler.classLoadHelper.class = org.quartz.simpl.CascadingClassLoadHelper

我试图理解为什么我在配置中更改旧的 ClassLoadHelper class 后仍然看到错误,事实证明我们的应用程序有第二个“隐藏”实例 运行 在我们的云提供商上,使用几个月前的旧版本代码。

由于 Quartz 作业 class 当时不存在,它反复尝试并未能触发触发器,因此在真实 up-to-date 使用的同一数据库中将触发器设置为错误状态实例。要解决这种情况,我们只需要摆脱无关的实例。

对于误导性信息,我深表歉意,我在发布此问题时并没有注意到其他情况。