最近调整了一下项目定时获取 etcd 配置的代码,本地测试没有问题,但是有的项目部署就是不运行,也不报错
了解了一下 ScheduledExecutorService 在抛出异常后会停止运行,于是我从这个地方找起
首先我调整了一下定时任务执行代码
schedule.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
refresh();
log.info("etcd config refresh successfully");
// 原
// } catch (Exception e) {
// 新
} catch (Throwable e) {
log.error("etcd config refresh failure", e);
}
}
}, TIME_CYCLE, TIME_CYCLE, TimeUnit.MILLISECONDS);
在有问题的机器上部署后,出现报错了,之前日志、控制台都没有报错,因为 NoSuchMethodError 继承自 Error,而我 catch 了 Exception,导致没有 catch 到,自动化的代码还是 catch Throwable 最好
NoSuchMethodError ConcurrentHashMap$KeySetView
[ERROR] [19:45:59] pool-4-thread-1 com.config.EtcdConfigService - etcd config refresh failure
java.lang.NoSuchMethodError: com.config.GeneralConfigGroup.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
at com.config.GeneralConfigGroup.putAll(GeneralConfigGroup.java:27)
at com.config.etcd.EtcdConfigGroup.loadNodeData(EtcdConfigGroup.java:92)
at com.config.EtcdConfigService.refresh(EtcdConfigService.java:132)
at com.config.EtcdConfigService$1.run(EtcdConfigService.java:109)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
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:744)
网上找了一下,发现这个问题是因为高版本的 jdk 编译的字节码在 低版本 jdk 上运行导致的,但是我看了下配置,pom.xml 里面的 jdk 版本是正确的 1.7,虽然我用的是 1.8。
看源码可以知道,jdk7 和 jdk8 对 keySet 的实现是不一样的,这也就是异常的原因了,和报错信息是对的上的
在这个老哥的帖子帮助下才知道,虽然语言级别设置的是 1.7 但是编译出的 class 并不能保证能在低版本jdk运行
Java高编译低运行错误(ConcurrentHashMap.keySet)
接下来,我把 jenkins 编译的 jdk 版本调整成了 jdk7,但是却报了另一个错误
原来 jenkins 支持的 jdk 最低版本是 jdk8,当我调整了编译 jdk 时,导致 jenkins 支持的代码也报错了
于是我重新建了一下任务,任务类型选择自由风格的软件项目,不要选 maven 项目(会涉及到 jenkins的支持代码),在新项目构建处 添加构建步骤 执行 shell,输入 mvn clean install,成功使用 jdk7 进行编译,ScheduledExecutorService etcd 刷新定时任务也成功执行了
在这里注意一下,可以指定 JAVA_HOME 变量,让 mvn 使用指定 jdk 进行编译,我们服务器上默认是 jdk7 就不需要配了
JAVA_HOME=/usr/local/jdk1.8.0_231
mvn -v