异步导出excel表格(1)
在现在的ERP系统中,数据库往往很大,批量导出几万甚至几十万条记录已经是很常见的事情。一般数据库里存在着上百万甚至上千万的数据,这种情况,对于一般的服务器,数据库查询时间要超过十分钟,对于复杂的查询操作,甚至几十分钟。这时对于客户端的请求,可能会等待很长时间才能返回响应。客户的感觉好像是系统没有响应。
针对大数据处理问题,ERP系统一般采用异步处理方式。
异步导出excel包括提交导出操作、处理导出操作、下载导出文件、定期删除生成的临时文件。
系统中用到的框架:jxls(主要用于生成excel)、quartz(定时删除临时文件)
用户请求操作流程基本如下:
用户提交任务后,保存任务数据(方便用户查询任务处理状态)到数据库,提交线程到ExcecutorService里。我采用的是mysql数据库,经过测试,发现执行一条SQL语句会占用CPU一个线程,并且在处理过程中,系统占用率一般在95%。如果多个线程导出操作同时处理,会造成系统CPU占用过量,ERP系统本身响应变的很慢或处在假死的状态。因此,我采用Executors.newSingleThreadExecutor 线程池(这个线程保持同一时间只有一个线程在处理,其它的依次排队等待)处理导出线程。
任务PMD如下:
数据获取类名、数据获取方法名、数据获取参数串三个字段主要用于处理系统在处理导出的过程中被异常关闭,下次系统启动时,会自动执行未执行完的操作。具体实现方法见后面章节。
文件模板编码,用于标识模板文件,方便以后扩展,此例中暂时不使用。
导出文件文件模板路径,主要保存excel模板的路径。
数据与模板关联文件,保存jxls上面关联规则beansMap
任务当前状态,任务状态:已提交、正处理、处理完成、处理失败。
任务文件是否已删除,标识当前导出文件是否已经被quartz删除(可以设置时间,过期文件会被Job删除)。
任务来源,标识属于哪类线程。
任务处理完成后,用户可以请求系统下载文件。
用户下载流程如下:
下载文件部分代码分享:
public void downloadFile(HttpServletRequest request, HttpServletResponse response) {
try {
String filePath = request.getParameter("filePath");
final String generateFilePrefix = request.getSession().getServletContext().getRealPath("/") + "export\\generate\\";
File outputFile = new File(generateFilePrefix + filePath);
long fileLength = outputFile.length();
if(!outputFile.exists()){
response.setCharacterEncoding( "GBK" );
response.setContentType("text/html");
response.getOutputStream().println(new String("找不到导出文件,请重新导出!".getBytes(), "ISO-8859-1") );
response.getOutputStream().flush();
response.getOutputStream().close();
return;
}
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=\""
+ new String(filePath.getBytes("GBK"), "ISO-8859-1")
+ "\"");
response.setHeader("Content-Length", String.valueOf(fileLength));
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
bis = new BufferedInputStream(new FileInputStream(generateFilePrefix + filePath));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[2048];
int bytesRead;
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
bis.close();
bos.close();
} catch(IOException e) {
e.printStackTrace();
try {
response.setCharacterEncoding( "GBK" );
response.setContentType("text/html");
response.getOutputStream().println(new String("导出失败!请联系管理员!".getBytes(), "ISO-8859-1") );
response.getOutputStream().flush();
response.getOutputStream().close();
} catch(IOException ioe) {
ioe.printStackTrace();
}
return;
}
}
上一篇: Android 图片加载框架Glide主流程源码分析
下一篇: 浅谈异步与同步