Quartz定时任务嵌套,创建不同的Scheduler实例问题
程序员文章站
2024-03-22 16:14:04
...
最近一个需求,在某网页上爬取图片和数据,要求一周爬一次,每次根据给出的xml文件里面里的参数去组装发送请求。我自己的做法是新建了两个JOB,一个JobA每周五启动,然后启动的时候JobA会启动另一个JobB,JobB会间隔若干秒去读取初始化加载到内存里的参数集合中的一个,然后发送请求道某网页获取返回的数据进行处理。等待全部参数都请求过后JobB直接停掉。 后续放到线上后发现第一次执行完后JobB停掉,等到第二次JobA该执行的时间却没有执行。猜测可能是JobA跟JobB共用了某个对象,这个对象对任务启动执行都是必须的,停掉JobB导致JobA的也不能再次执行了(注:使用的是scheduler.shutdown();)方法 查看quartz源码以及自己代码,发现自己创建的时候共用了一个名字为"DefaultQuartzScheduler"的scheduler实例,导致停掉一个另外一个也不能正常运行了。所以这里,需要修改自己的代码,创建多个不同的shcheduler实例,不同实例的关闭和启动互不影响. 如果直接用SchedulerFactory sf = new StdSchedulerFactory();Scheduler scheduler = sf.getScheduler();获取的scheduler是从仓储SchedulerRepository里面取出来的,仓储注册scheduler的时候为每个scheduler分配一个唯一的名字,问题就在于这里,Quartz默认有个配置文件quartz.properties,里面定义了这个名字,这样的话,每次通过sf.getScheduler()获取的都是同一个实例,(假设你认为get出来的都是不同实例)把其中的某一个shceduler shutdown的话,其他scheduler也会关闭掉,因为它们其实是同一个实例!尝试使用:sf.getScheduler("给定名字")发现返回的都是null,翻看源码,此方法并没有进行创建对象的逻辑。而仅仅是获取指定名字的scheduler实例而已。查看源码和资料,Quartz是配置文件初始化的,于是翻到了Quartz的默认配置文件(在其jar包里)quartz.properties。
配置文件如下:
#-----集群的配置
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
#------配置调度器的线程池
#线程池类
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#线程个数
org.quartz.threadPool.threadCount = 10
#线程优先级
org.quartz.threadPool.threadPriority = 5
#
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#
org.quartz.jobStore.misfireThreshold = 60000
#------配置任务调度现场数据保存机制,默认保存在内存
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
于是,可以自己显示的初始化,如下显式使用StdSchedulerFactory,调用其initialize方法,并且自己定义填写配置内容:
StdSchedulerFactory sf = new StdSchedulerFactory();
Properties props = new Properties();
props.put("org.quartz.scheduler.instanceName", "你定义的名字");
props.put("org.quartz.threadPool.threadCount", "10");//必填
props.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
sf.initialize(props);
scheduler = sf.getScheduler();
System.out.println(scheduler.getSchedulerName());
scheduler.shutdown();
用上述方法新建的两个不同的scheduler实例后,job的嵌套,多个job使用的关闭启动就互不影响了。总结一下:
1、默认无参构造方法获取的StdSchedulerFactory.getScheduler()获取到的是一个实例名为DefaultQuartzScheduler的调度器,如果用这个调度器启动多个job,一个job关闭这个调度器会导致其他job也无法再次运行。所以涉及到job的嵌套且希望不同的job启动关闭互不影响的话就需要自己自定义初始化获取不同的调度器实例
2、scheduler是一个计划调度器容器,容器里面可以盛放众多的JobDetail和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行。
3、JobDetail是一个可执行的工作,它本身可能是有状态的。
4、Trigger代表一个调度参数的配置,什么时候去调。
5、当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的作业(JobDetail和Trigger所组成的一对),就可以伴随容器启动而调度执行了。
6、scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业,这样可以提高容器效率。
7、将上述的结构用一个图来表示,如下: