最近在应用 COLA 框架自带的异步工作时,发现每次执行异步都执行了两次,如果一些没有做幂等的接口,这样是会有问题的,比方入库操作之类的,就会造成数据反复入库,造成重大 bug。
带着纳闷,开始了 bug 之旅。
1 问题发现
1、首先排查执行入口,是不是有两个,发现只有一个;
2、调用入口的问题?间接通过 controller 调用 handler,还是调用了两次。
3、简化代码,把 handler 内的内容都删掉,只有一个 logger 打印语句?后果还是打印了两次。
然而这次,发现 logger 的线程名不一样,是两个线程。
2021-07-26 14:11:19.429 INFO 47294 --- [pool-4-thread-2] c.e.colademo.event.handler.TestHandler : >>>>>>>>>>>>> 0
2021-07-26 14:11:19.430 INFO 47294 --- [pool-4-thread-1] c.e.colademo.event.handler.TestHandler : >>>>>>>>>>>>> 0
2 问题排查
为什么会有两个线程同时执行呢?查看 COLA 源码。
public void asyncFire(EventI event) {this.eventHub.getEventHandler(event.getClass()).parallelStream().map((p) -> {
Response response = null;
try {if (null != p.getExecutor()) {p.getExecutor().submit(() -> {return p.execute(event);
});
} else {this.defaultExecutor.submit(() -> {return p.execute(event);
});
}
} catch (Exception var5) {response = this.handleException(p, response, var5);
}
return response;
}).collect(Collectors.toList());
}
提交异步工作,最终都走到下面的代码,将工作提交到线程池执行,如果没有自定义线程池,那么会提交到defaultExecutor
这个默认线程池中。
发现提交了两遍,查看 this 对象中的内容,== 发现 Event 对象和 Handler 对象都有两个 ==。
3 问题起因
是什么起因会造成反复对象呢?
比照之前的 handler 对象,这个对象惟一的不同就是应用@RefreshScope
,查看注解源码,发现应用了 == 这个注解的对象,都会应用代码创立一个新的对象,并缓存起来 ==,debug 源码,查看缓存的对象。
发现确实有 TestHandler 对象,对象为 @12349。
比照图 1 中的 handler 对象,外面也有一个 TestHandler 对象,对象也是 @12349.
原来如此,因为应用了注解@RefreshScope
,这个注解会创立一个对象,这样就会有两个雷同的对象,造成反复执行。
论断:应用注解 @RefreshScope
须要留神,最好把获取配置的内容放在独自的 property 对象中,不要和其余代码混用。