springboot框架使用@Scheduled注解实现的单线程定时任务卡死,原因一般是没有设置超时策略导致无限期等待。
定位问题代码
使用jstack获取应用线程快照
# 1.服务器上找一下文件在哪 [root@iZ2ze616zb6x1rqtgc7w9qZ ~]# which java /home/jdk8/jdk1.8.0_271/bin/java [root@iZ2ze616zb6x1rqtgc7w9qZ ~]# cd /home/jdk8/jdk1.8.0_271/bin/ [root@iZ2ze616zb6x1rqtgc7w9qZ bin]# ls |grep jstack jstack # 2.查看应用的进程ID:23417 [root@iZ2ze616zb6x1rqtgc7w9qZ bin]# ps -ef|grep ruoyi root 20580 20029 0 16:02 pts/0 00:00:00 grep --color=auto ruoyi root 23417 1 0 Jul19 ? 00:42:48 java -Xms128m -Xmx512m -jar ruoyi-admin.jar # 3.生成线程快照 [root@iZ2ze616zb6x1rqtgc7w9qZ bin]# jstack -l 23417 > /home/ruoyi-dump.txt
查看ruoyi-dump.txt文件中定时任务线程状态和发生问题的代码位置
"pool-2-thread-16" #66 prio=5 os_prio=0 tid=0x00007f92a0003000 nid=0x541b waiting on condition [0x00007f9265743000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at com.suwell.ofd.custom.agent.HTTPAgent.convert(HTTPAgent.java:167) at com.suwell.ofd.custom.agent.ConvertAgent.convert(ConvertAgent.java:494) at com.jiangk.apilicense.service.CreateOfdService.makeOFD(CreateOfdService.java:595) at com.jiangk.apilicense.service.CreateOfdService$$FastClassBySpringCGLIB$$9bc09320.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
根据第二步可以定位定时任务卡死在CreateOfdService.java:595行
... Packet packet = new Packet(PackType.COMMON, Target.OFD); packet.file(new Common(null, "ofd", new FileInputStream(srcfile_final))); packet.seal("Suwell_WebOES", null,seal); # 这个方法是调用外部服务生成OFD文件,一直没有相应导致定时任务卡死。 haseal.convert(packet,new FileOutputStream(ofdoutpath_final)); ...
解决办法
调用外部请求增加超时处理,示例
// 为外部请求创建一个单线程 ExecutorService executor = Executors.newSingleThreadExecutor(); Callable<String> call = new Callable<String>() { @Override public String call() throws Exception { //开始执行耗时操作 haseal.convert(packet, new FileOutputStream(ofdoutpath_final)); return "证照转换方法“haseal.convert”执行执行完成!"; } }; try { Future<String> future = executor.submit(call); future.get(1000 * 10, TimeUnit.MILLISECONDS); //任务处理超时时间设为 10 秒 } catch (TimeoutException ex) { System.out.println("OFD转换服务处理超时...."); throw new ResourceNotFoundException(gridDataDTO.getIdcard() + " OFD转换服务调用超时!"); }finally { // 关闭线程池 executor.shutdown(); }