Spring Quartz 远程调度程序

Spring Quartz remote scheduler

我使用 spring 启动了石英服务器实例(如 article),效果很好。 但是当我尝试使用 quartz 客户端启动另一个应用程序并连接到 quartz 服务器时,出现错误,请帮助我做错了什么。

服务器配置:

@Configuration
public class ServerConfig {
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(ApplicationContext applicationContext) {
        SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
        schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));
        schedulerFactory.setSchedulerListeners(...);
        schedulerFactory.setJobFactory(...);
        schedulerFactory.setTriggers(...);
        return schedulerFactory;
    }
    ...

}

客户端配置:

@Configuration
public class ClientConfig {
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
        schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));
        return schedulerFactory;
    }
...
}

服务器quartz.properties:

org.quartz.scheduler.instanceName=myScheduler
org.quartz.scheduler.rmi.export=true
org.quartz.scheduler.rmi.createRegistry=true
org.quartz.scheduler.rmi.registryHost=localhost
org.quartz.scheduler.rmi.registryPort=1099
org.quartz.scheduler.rmi.serverPort=1100

客户quartz.properties:

org.quartz.scheduler.instanceName=myScheduler
org.quartz.scheduler.rmi.proxy=true
org.quartz.scheduler.rmi.registryHost=localhost
org.quartz.scheduler.rmi.registryPort=1099

服务器日志:

[2019-09-03] [10:07:26.171] INFO  SchedulerFactoryBean:538 - Loading Quartz config from [class path resource [quartz.properties]]
[2019-09-03] [10:07:26.229] INFO  StdSchedulerFactory:1208 - Using default implementation for ThreadExecutor
[2019-09-03] [10:07:26.272] INFO  SchedulerSignalerImpl:61 - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
[2019-09-03] [10:07:26.273] INFO  QuartzScheduler:229 - Quartz Scheduler v.2.3.0 created.
[2019-09-03] [10:07:26.277] INFO  RAMJobStore:155 - RAMJobStore initialized.
[2019-09-03] [10:07:26.338] INFO  QuartzScheduler:421 - Scheduler bound to RMI registry under name 'schedulerFactoryBean_$_NON_CLUSTERED'
[2019-09-03] [10:07:26.343] INFO  QuartzScheduler:294 - Scheduler meta-data: Quartz Scheduler (v2.3.0) 'schedulerFactoryBean' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - access via RMI.
...
[2019-09-03] [10:07:26.344] INFO  StdSchedulerFactory:1362 - Quartz scheduler 'schedulerFactoryBean' initialized from an externally provided properties instance.
[2019-09-03] [10:07:26.345] INFO  StdSchedulerFactory:1366 - Quartz scheduler version: 2.3.0
...
[2019-09-03] [10:07:29.12] INFO  SchedulerFactoryBean:684 - Starting Quartz Scheduler now
[2019-09-03] [10:07:29.13] INFO  QuartzScheduler:547 - Scheduler schedulerFactoryBean_$_NON_CLUSTERED started.

客户端日志:

[2019-09-03] [10:11:53.255] INFO  SchedulerFactoryBean:538 - Loading Quartz config from [class path resource [quartz.properties]]
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerFactoryBean' defined in class path resource [com/mypackage/ClientConfig.class]: Invocation of init method failed; nested exception is org.quartz.SchedulerException: Operation not supported for remote schedulers.
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1631)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:312)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:742)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:73)
    ... 3 more
Caused by: org.quartz.SchedulerException: Operation not supported for remote schedulers.
    at org.quartz.impl.RemoteScheduler.getListenerManager(RemoteScheduler.java:913)
    at org.springframework.scheduling.quartz.SchedulerAccessor.registerListeners(SchedulerAccessor.java:340)
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:476)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1689)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1627)
    ... 13 more

如您所见,错误是: org.quartz.SchedulerException: Operation not supported for remote schedulers. 并在 bean 初始化时发生。怎么了?

客户端启动失败后,服务器在日志中显示:

[2019-09-03] [10:11:53.462] INFO  QuartzScheduler:666 - Scheduler schedulerFactoryBean_$_NON_CLUSTERED shutting down.
[2019-09-03] [10:11:53.462] INFO  QuartzScheduler:585 - Scheduler schedulerFactoryBean_$_NON_CLUSTERED paused.
[2019-09-03] [10:11:53.871] INFO  QuartzScheduler:447 - Scheduler un-bound from name 'schedulerFactoryBean_$_NON_CLUSTERED' in RMI registry
[2019-09-03] [10:11:53.872] INFO  QuartzScheduler:740 - Scheduler schedulerFactoryBean_$_NON_CLUSTERED shutdown complete.

我有 Spring - 4.3.20,Quartz - 2.3.0

更新: 我看到 registerListeners() 调用 SchedulerFactoryBean:

@Override
public void afterPropertiesSet() throws Exception {
        ...
        try {
            registerListeners();
            registerJobsAndTriggers();
        }
        catch (Exception ex) {
            try {
                this.scheduler.shutdown(true);
            }
            catch (Exception ex2) {
                logger.debug("Scheduler shutdown exception after registration failure", ex2);
            }
            throw ex;
        }
}

registerListeners() 中我们调用 getScheduler().getListenerManager() 失败并出现 Operation not supported for remote schedulers 异常。如何避免为 RemoteScheduler 调用 registerListeners()

最后我按照以下方式让它工作:

  • 我拒绝在客户端上使用 Spring 的 SchedulerFactoryBean,而是直接从 quartz 库中创建 StdSchedulerFactory 的实例(然后它正确创建 RemoteScheduler) 的实例:new StdSchedulerFactory()。它正确连接到服务器石英实例。

  • 由于我无法从 SchedulerFactoryBean 获得 StdSchedulerFactory 的实例,所以我只是将两者都传递给需要的 bean。所以在客户端应用程序上 SchedulerFactoryBean 为空,在服务器应用程序上 StdSchedulerFactory 为空。

  • 两家工厂的工作计划部分是相同的,因为我对他们所做的只是获取计划程序:

private Scheduler getScheduler() throws SchedulerException {
    if (schedulerFactoryBean != null) return schedulerFactoryBean.getScheduler();
    if (schedulerFactory != null) return schedulerFactory.getScheduler();
    else return null;
}

然后...

Scheduler scheduler = getScheduler();
if (scheduler != null) {
    scheduler.scheduleJob(job, trigger);
}