线上有一个服务,采用ScheduledExecutorService定时任务刷新数据库数据到本地缓存作为路由信息
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {public void run() {hello();}}, 10, 10, TimeUnit.SECONDS);
scheduleWithFixedDelay以及后面两个10的参数分别表示延迟10s执行,之后每次都以10s间隔执行(就是不停的往队列里丢任务),但是这个方法是需要任务成功后再延迟10s,所以真正的间隔应该是10s+任务执行的实际时间。
(题外话:如果想只以固定时间执行应该用scheduleAtFixedRate,但是如果任务没有在指定延迟时间内完成,可能导致任务重叠)
然后最近发现改库后这个缓存更新的很不及时,尤其是有一次新需求上线,影响了线上展示,而且这个服务属于基础服务,非业务服务,一出问题很难想到他身上,最后是通过线上禁用机器debug才发现的,路由服务没有刷新!
当下先做了重启(重启后会重新恢复这个定时刷新任务)
后面进行排查,我的排查思路也比较简单,观察其执行内容(报错信息太多了都淹没在日志里了),判断其调用量
发现该任务固定执行两条sql,通过sql执行成功率发现,一但某次sql获取链接超时或者执行失败(比如网络抖动,太正常了,运维升级什么的很容易抖动)就导致不再执行sql。
基本可以猜测到是异常处理没做好,先做了一些非空判断保证不报错,然后又加了try catch做兜底,之后服务上线,后面再没出现过此类问题。
知识点:
一个ScheduledExecutorService启动的Java线程无故挂掉的原因是:如果使用者抛出异常,ScheduledExecutorService 将会停止线程的运行,而且不会报错,没有任何提示信息。解决方法是:try-catch将异常信息打印,或者用ScheduledFuture<?>获取线程运行结果。
引申一下之前看到的,线程池提交任务有两种 exetute和submit
exetute如果发生异常后会销毁当前线程,并重新起线程维持核心线程数
submit如果发生异常不会销毁,如果使用future.get方法里面会有异常信息