说道定时任务,你们立马会想到Quartz,强大的任务调度引擎和时间规则配置让咱们只须要专一于业务,并且使用极其方便,但是最近中信云上就栽了一个大跟头。java
中信云有测试环境和正式环境,定时任务框架在测试环境上跑的很溜,可是一更新到正式环境加载初始化就报错,报错信息以下:apache
Couldn't generate instance Id! org.quartz.SchedulerException: Couldn't get host name! [See nested exception: java.net.UnknownHostException: iZ2ze6wfyo1q58e7lo99kvZ: iZ2ze6wfyo1q58e7lo99kvZ] at org.quartz.simpl.SimpleInstanceIdGenerator.generateInstanceId(SimpleInstanceIdGenerator.java:36) at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1184) at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1484) at net.luculent.liems.business.bd.util.BdTaskServlet.init(BdTaskServlet.java:36) at javax.servlet.GenericServlet.init(GenericServlet.java:158) at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1282) at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1195) at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1085) at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5318) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5610) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1572) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1562) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745)
看到这个报错后,猜想应该和环境有问题,上网查找解决办法都是出奇的一致,以为问题不是大问题,解决办法以下:api
进入线上环境(centOs6.x): 1, 查看主机名(命令) : hostname 2, 打开hosts文件: vi /etc/hosts 查看是否有乱码等异常内容 3, 确保有: 127.0.0.1 后跟上面用hostname命令查看到的主机名, 若是没有则加上 如 : hostname -> USER-1234 则: 127.0.0.1 USER-1234
正式环境上配置后效果立杆见影,报错没有了,接下来验证业务,可是全部全部程序都打不开了,找来日志发现是受权失败了,打开刚刚配置的文件app
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 iZ2ze6wfyo1q58e7lo99kvZ ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
发现以前的配置都是带了域名的,因而我也加了一个域名框架
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 iZ2ze6wfyo1q58e7lo99kvZ.localdomain ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
结果是受权能够了,可是定时任务又报了相同的错,因而总结出了我想要的配置和受权的配置起了冲突,我这里不能改受权,因而只能从定时任务下手了dom
报错的地方是初始化任务计划的id,在StdSchedulerFactory里面找到了这个报错的这个类ide
if (schedInstId.equals(AUTO_GENERATE_INSTANCE_ID)) { autoId = true; instanceIdGeneratorClass = cfg.getStringProperty( PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS, "org.quartz.simpl.SimpleInstanceIdGenerator"); } else if (schedInstId.equals(SYSTEM_PROPERTY_AS_INSTANCE_ID)) { autoId = true; instanceIdGeneratorClass = "org.quartz.simpl.SystemPropertyInstanceIdGenerator"; }
public class SimpleInstanceIdGenerator implements InstanceIdGenerator { public String generateInstanceId() throws SchedulerException { try { return InetAddress.getLocalHost().getHostName() + System.currentTimeMillis(); } catch (Exception e) { throw new SchedulerException("Couldn't get host name!", e); } } }
public class SystemPropertyInstanceIdGenerator implements InstanceIdGenerator { /** * System property to read the instanceId from */ public static final String SYSTEM_PROPERTY = "org.quartz.scheduler.instanceId"; /** * Returns the cluster wide value for this scheduler instance's id, based on a system property * @return the value of the {@link SystemPropertyInstanceIdGenerator#SYSTEM_PROPERTY system property} * @throws SchedulerException Shouldn't a value be found */ public String generateInstanceId() throws SchedulerException { String property = System.getProperty(SYSTEM_PROPERTY); if(property == null) { throw new SchedulerException("No value for '" + SYSTEM_PROPERTY + "' system property found, please configure your environment accordingly!"); } return property; } }
顿时思路如泉涌,感叹人家代码的设计好灵活,就像为我这个尴尬境遇专门设计的,也才反应过来测试环境是单机的,正式环境是集群的,测试环境就没有调用这个方法,因而决定使用SystemPropertyInstanceIdGenerator 来初始化id,找出了配置文件quartz.properties 修改了instanceId为SYS_PROP测试
org.quartz.scheduler.instanceName = scheduler org.quartz.scheduler.instanceId = SYS_PROP 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.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.maxMisfiresToHandleAtATime=10 org.quartz.jobStore.isClustered = true
修改了catalina.sh添加了系统属性this
JAVA_OPTS="$JAVA_OPTS -Dorg.quartz.scheduler.instanceId=01"
重启环境,终于实现世界和平了.net
总结:有时候咱们在解决问题彻底没有思路的时候就须要从源码入手,这就须要咱们平时多看看一些开源的框架源码,初期不建议打开代码一行一行过,能够经过官方的api文档来阅读各个方法做用,经过各类场景代码来跟踪调试,在后面咱们会分享Quartz的实现原理,欢迎关注。