`
Everyday都不同
  • 浏览: 714300 次
  • 性别: Icon_minigender_1
  • 来自: 宇宙
社区版块
存档分类
最新评论

解决一个web的项目定时任务入库“卡住”问题

阅读更多

事情的起因是因为我要在一个定时任务的某处将数据入库,一个是批量入库,之后再是非批量的普通方式入库,伪代码如下:

 

for(int i=0; i<n; i++) {
    //批量入库
pools.execute(new BatchSaveThread(list));//list为map集合

//普通入库
pools.execute(new SaveThread(map));

}

 这个定时任务是每隔1min就触发的,频率还蛮高。而且n大约是10,pools为线程池,BatchSaveThread和SaveThread分别为批量入库的线程和普通入库的线程。

 

问题现象:这个定时任务放到环境上跑,发现过个5-7min就会“卡死”,即:批量入库或普通入库都不能持续,普通入库在持续最多7分钟左右就不再进行了。debug发现普通入库的线程会进入,但是就是没法往下执行。

 

解决:其实可以从线程的角度考虑,之前想在定时任务当中,用线程池调配线程的方式异步地入库,但由于批量入库的步骤可能需要的时间较长,所以线程在此处耗费时间较多,以至于在本分钟内没能完成,卡在这导致普通入库那一步也没有完成。

把普通入库和批量入库分为2个定时任务,一前一后的执行。普通入库先执行,在那里for循环中负责生成每次跟循环有关的list,并把他们放到内存中,之后,在晚些时候执行批量入库,再在批量入库的定时任务中for循环,取出每次的list,再针对该List入库。为进一步避免线程问题,可以摈弃异步入库,直接取代原来的入库线程而调用dao的相关入库方法,,后来发现其实也并没有消耗太多时间。

注:

1)内存存取list可以使用ServletContext的setAttribute(String key, Object obj)和getAttribute(key);取完可以removeAttribute(String key)减少内存占用~~;key的设计可以带上for循环里的元素以及时间。

2)mysql中想要真正的executeBatch()起作用,要记得在jdbc的url上加上rewriteBatchedStatements=true才行,否则批量执行是不会起作用的。

改进之后,经过长时间测试发现,无论是数据量较大的批量入库,还是普通入库,都能平稳地运行下去了。

 

顺便补充些小问题:

1.循环一个集合的前提是这个集合不为null,虽然默认情况下是根本不用考虑的,但如果你是通过本例中ServletContext的getAttribute(String key)这种方式,或其他方式得到List——即不是通过常见的定义方式得到的List,在循环它之前,需要做容错处理:if(list != null)再循环哦!

2.在一个定时任务类的属性定义上不要直接带上需要通过bean得到的其他bean类,否则会报空指针异常。比如我在一个定时任务类的的属性上(方法外)这样:

WebApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();

 或这样:

Dao dao = bean.get(xxxx)

 都会报NullPointerException。之所以不能在方法体外的类属性上直接定义这些,是因为:定时任务类也是被配置成bean的,在Spring容器启动成功之前,整个Spring容器的环境是还未完全搭建成功的,而bean的这些属性会在把它管理之前就扫描到,所以你不能在还未完全搭建Spring环境成功就使用Spring的上下文。——这点需注意,可以延伸到任何一个需要被管理的bean,不能在方法体外部直接使用Spring上下文

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics