白天时服务器一般收到用户的请求较多,执行比较多的任务。晚上收到用户的请求少,压力小,可以在夜间执行一些耗时的操作,如生成报表。本文将介绍,在多公司,一个公司对应一个数据库的情景下,如何在夜间执行任务,生成各个公司的报表。本文将介绍以下内容:“创建运行报表的相关类”、“报表类的执行逻辑”、“启动所有报表线程”。
1、创建运行报表的相关类。
TaskThread继承于Thread,是一个线程类。所有类型的报表继承于TaskThread类,TaskThread存放公共属性和方法:
@Component("taskThread")
@Scope("prototype")
public class TaskThread extends Thread
TaskThread的run方法
@Override
public void run() {
while (atomicInteger.get() != SIGNAL_ThreadExit) {
doTask();
try {
if (isMockingMVC) {
timeSpanToCheckTaskStartTime = 50; // 在测试环境下,检查频繁一点,以缩短报表测试的运行时间
} else {
timeSpanToCheckTaskStartTime = 10000; // 检查疏一点,以免占用太多CPU时间
}
Thread.sleep(timeSpanToCheckTaskStartTime);
} catch (InterruptedException e) {
logger.error("夜间检查任务【" + name + "】定时任务线程异常" + e.getMessage());
return;
}
}
}
Run方法调用了doTask方法,
doTask方法执行的逻辑:
(1)遍历所有公司;
(2)判断是否已经到了执行该任务的时间dateToRunReport;
(3)如果到了执行任务的时间,那么设置下一次执行任务的时间是24小时候dateToRunNextTime;
(4)开始执行doTaskForEveryCompany:
protected void doTask() {
List<BaseModel> list = CacheManager.getCache(BaseAction.DBName_Public, EnumCacheType.ECT_Company).readN(false, false);
for (BaseModel bm : list) {
Company company = (Company) bm;
if (!company.getDbName().equals(BaseAction.DBName_Public)) {// 公共DB没有夜间任务
if (company.getIncumbent() == EnumCompanyCreationStatus.ECCS_Incumbent.getIndex() && company.getStatus() == EnumCompanyCreationStatus.ECCS_Incumbent.getIndex()) {// 表示创建公司时是否已经创建完成
Date dateToRunReport = (getReportDateForTest() == null ? new Date() : getReportDateForTest());
if (mapDatetimeForNextRun.get(company.getDbName()) == null) {
mapDatetimeForNextRun.put(company.getDbName(), getStartDatetime(company.getDbName()));// 有的公司是后来创建的
}
// 如果时间已经超过本夜间任务设定的运行时间,则设置此时间为24小时后再跑一次,然后才跑夜间任务。子类中,可以自定义夜间任务跑或不跑、如何跑。
if (DatetimeUtil.isAfterDate(dateToRunReport, mapDatetimeForNextRun.get(company.getDbName()), 0) || canRunOnceForTest()) {
logger.info("定时任务执行时间段已到,开始运行任务, \t本线程hashcode=" + this.hashCode());
Date dateToRunNextTime = mapDatetimeForNextRun.get(company.getDbName());
dateToRunNextTime = DatetimeUtil.getDate(dateToRunNextTime, 24 * 3600);
mapDatetimeForNextRun.put(company.getDbName(), dateToRunNextTime);
doTaskForEveryCompany(company);
taskStatusForTest.incrementAndGet();
}
}
}
}
}
2、报表类的执行逻辑。
以销售商品分类占比报表为例,介绍报表的生成过程。
类名为RetailTradeDailyReportByCategoryParentTaskThread,继承了TaskThread:
@Component("retailTradeDailyReportByCategoryParentTaskThread")
@Scope("prototype")
public class RetailTradeDailyReportByCategoryParentTaskThread extends TaskThread
因为它是TaskThread的子类,所以它的关键方法为doTaskForEveryCompany: