有时需要批量处理任务,希望打印相关的任务处理信息。写了个工具类。先看效果,后贴代码。
任务进度信息打印效果如下:
业务关键代码:
ProgressUtil pu = new ProgressUtil("导入数据", list.size());
for (Record record : list) {
in.put(1, record.getStr("mkid"));
// logger.info(record.getStr("mkid") + "开始");
reportMklbService.callProc(sql, in, out);
// logger.info(record.getStr("mkid") + "结束");
pu.finishOne("mkid:"+record.getStr("mkid"));
}
工具代码ProgressUtil.java:
package com.xpl.util;
import org.apache.log4j.Logger;
/***
* 进度工具
* @author xpl
*
*/
public class ProgressUtil {
private static final Logger logger = Logger.getLogger("");
// 工作内容
String title;
// 工作量化后总数量
int total;
// 已完成的数量
int finish = 0;
long startTime;
long preTime;
long nextTime;
//完成一项工作后是否自动休眠
boolean autoSleep = false;
//上一次休眠时长
long preSleepTime;
//休眠耗时。参考sleep()方法注释
long [] timeArr = {8000,3000,2000,1000,4000,5000,6000,7000,8000};
//完成指定数量。参考sleep()方法注释
int [] countArr = {1,10,20,40,60,80,100};
public ProgressUtil(String title, int total) {
this.title = title;
this.total = total;
startTime = System.currentTimeMillis();
preTime = startTime;
}
public ProgressUtil(String title, int total, boolean autoSleep) {
this(title, total);
this.autoSleep = autoSleep;
}
/***
* 失败一次,打印信息。
*/
public void failureOne(String errorMsg) {
logger.error(title + " | " + total + " / " + finish + " | " + "休眠时长:"+ preSleepTime +"|错误信息:"+errorMsg);
}
/***
* 完成一个任务后,打印信息
*/
public void finishOne() {
finishOne("");
}
/***
* 完成一个任务后,打印信息
* @param taskInfo
* 任务信息
*/
public void finishOne(String taskInfo) {
finish++;
long sleepTime = sleep();
nextTime = System.currentTimeMillis();
String str = title + " | 耗时" + getHs(nextTime - preTime) + " | " + total + " / " + finish;
str += " | 剩余时间:" + getHs(remainingTime());
str += " | " + taskInfo;
if(finish == total){
str += " | 总耗时" + getHs(nextTime - startTime);
}
if(sleepTime > 0){
str += " | 休眠" + getHs(sleepTime);
}
logger.info(str);
// System.out.println(str);
preTime = nextTime;
}
/***
* 耗时
* @param l
* @return
*/
public String getHs(long hs) {
if (hs < 1000) {
return hs + "毫秒";
} else if (hs < 1000 * 60) {
return (hs / 1000) + "秒";
} else if (hs < 1000 * 60 * 60) {
return (hs / (1000 * 60)) + "分" + ((hs % (1000 * 60)) / 1000) + "秒";
} else if (hs < 1000 * 60 * 60 * 60) {
return (hs / (1000 * 60 * 60)) + "时" + ((hs % (1000 * 60 * 60)) / (1000 * 60)) + "分";
}
return hs + "毫秒";
}
/***
* 自动休眠,autoSleep为true时开启。
* 默认:
* long [] timeArr = {0,1000,2000,3000,4000,5000,10000};
* int [] countArr = {1,3,5,10,20,50,100};
* 含义:完成数finish小于等于countArr[i]时,休眠timeArr[i]时长。
* finish 大于countArr[countArr.length - 1]时,休眠timeArr[timeArr.length - 1]时长。
*/
private long sleep(){
preSleepTime = 0;
if(autoSleep && finish < total){
int i = 0;
for (; i < countArr.length; i++) {
if(finish <= countArr[i]){
break;
}
}
try {
if(i < timeArr.length){
preSleepTime = timeArr[i];
}else{
preSleepTime = timeArr[timeArr.length - 1];
}
Thread.sleep(preSleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return preSleepTime;
}
/***
* 估计剩余时间
* 先根据已完成任务数量,求完成一次的平均时间,然后根据待完成任务数估计完成所有任务剩余时间。
* @return
*/
public long remainingTime() {
return (nextTime - startTime)/finish * (total - finish);
}
}
autoSleep 如果为true,则每次完成任务后会自动休眠一段时间。这个主要是网络上批量爬取数据时,避免访问太过频繁被屏蔽时使用。
failureOne 一般在业务代码中try catch中使用,抛出异常或者有任务执行失败的标志时调用。可以清楚的知道哪个任务出错了。
例子:
/***
* 爬取并保持章节内容
* @param rm
* @param list
*/
public void crawlAndSaveContent(ResponseMap rm, List<Mulu> list) {
int size = list.size();
ProgressUtil pu = new ProgressUtil("更新章节内容", size, true);
for (Mulu mulu : list) {
try{
muluService.updateContent(rm, mulu);
} catch (Exception e) {
e.printStackTrace();
pu.failureOne(e.getMessage()+"|"+mulu.getUrl());
}
pu.finishOne(mulu.getUrl());
size--;
}
}