摘要

通过缓存和维护Excel Workbook实例,极速响应Excel数据报表请求。

这是一个真实的大数据“云计算”项目中的解决方案,在给定的时间和资源下,只有这种方法是最简单并且是可行的。

XX公司的需求

简单Excel报表:1个sheet。5秒内可以在界面展示。

复杂Excel报表:7个sheet。20秒内可以在界面展示。

并发响应:20个用户

详细介绍

每个sheet中有大量的Excel公式,如 F=E32-[35764%D1]*[DD]/[LD]。

每个单元格的值,需要后台从Mongodb数据库中取出并计算。

Web前端获得后台返回的数据,插入到Excel单元格中,然后Excel自动执行公式,然后将Excel完整地显示到界面中。

时间瓶颈

后台数据获取和数据计算的时间:

1个sheet要3-4s,7个sheet要10到20s。

前台展示剩下的时间:
1个sheet:1到2秒
7个sheet:0到10秒

读取Excel文件需要的时间

1个sheet:1s左右

7个sheet:2s左右

Excel文件构造成POI XSSFWorkbook对象

1个sheet:1s左右

7个sheet:7s左右

最初,我以为是IO有瓶颈,后来发现不是。主要是POI 构造XSSFWorkbook对象,需要花费很长时间。

XSSFWorkbook这可是一个比较大且复杂的数据结构,维护着一个Excel文档的所有信息。

(令人可气的是,测试机Linux服务器性能竟然还不如我的笔记本)

解决方案

考虑到并发请求最多也就是20,而且这只是一个Demo项目,因此“空间换时间”是可行的。

灵感来源:数据库连接池、对象池、缓存等都可以用来提高程序的性能。

最终方案维护一份Workbook资源池。程序启动后,写一个定时器,监控资源池中的对象数量,达到资源下界的时候,就构造一些新的对象放进资源池中。

与数据库连接池等不同的是,Workbook资源池中的每一个XSSFWorkbook对象,只能使用一次。用完后,就必须从资源池中删除。

因为,XSSFWorkbook被使用后,很多状态都变了。

资源池代码结构

public class WorkbookPool {

    private static int complexMinSize = TemplatePropertyReader.COMPLEX_MIN_SIZE;
// 复杂报表--最大缓存
private static int complexMaxSize = TemplatePropertyReader.COMPLEX_MAX_SIZE; public WorkbookPool() { } public void init() { simpleReportTimer();
complexReportTimer();
} // 定时器任务---创建报表对象 private void simpleReportTimer() {
log.info("Simple Producter Timer Start...");
Timer producterTimer = new Timer();
// 在1秒后执行此任务,每次间隔1秒
producterTimer.schedule(new SimpleProducterTask(), 1000, 1000); } private void complexReportTimer() {
log.info("Complex Producter Timer Start...");
Timer complexProducterTimer = new Timer();
// 在1秒后执行此任务,每次间隔1秒
complexProducterTimer.schedule(new ComplexProducterTask(), 1000, 1000);
} // 简单报表--定时器任务
class SimpleProducterTask extends TimerTask { public void run() { int simpleSize = simpleVector.size();
if (simpleSize <= simpleMinSize) {
int toBuildSize = simpleMaxSize - simpleSize;
log.info("before : simpleSize=" + simpleSize + ",toBuildSize=" + toBuildSize);
buildSimple(toBuildSize);
log.info("after : simpleSize=" + simpleVector.size());
} }
} // 复杂报表--定时器任务
class ComplexProducterTask extends TimerTask { public void run() {
int complexSize = complexVector.size();
if (complexSize <= complexMinSize) {
int toBuildSize = complexMaxSize - complexSize;
log.info("before: complexSize=" + complexSize + ",toBuildSize=" + toBuildSize);
buildComplex(toBuildSize);
log.info("after: complexSize=" + complexVector.size());
}
}
} }

启示

数据库连接池、对象池、缓存等很多程序设计中的概念,是存在着相似之处的。

学习和借鉴每一个成熟的概念和解决方案,能够产生更多的好方法。

活学活用,灵活解决实际工作中遇到的问题。

拒绝码农,谢绝书呆子。

相关阅读

ExcelToHtmlTable转换算法:将Excel转换成Html表格并展示(项目源码+详细注释+项目截图)

码农:客户是恶魔

原文参见http://FansUnion.cn/articles/2851 小雷网(FansUnion.cn)

极速响应Excel数据报表请求的一种方法的更多相关文章

  1. SqlServer表EXCEL数据复制的另一种方法

    一个.SqlServer表中的数据复制到excel 1.新建查询,用sql语句把表数据读出来 2.然后,选择数据,右键.复制(也能够点击连同标题复制),拷贝到记事本中(不然会乱码) 3.然后再把记事本 ...

  2. php发送post请求的三种方法示例

    本文分享下php发送post请求的三种方法与示例代码,分别使用curl.file_get_content.fsocket来实现post提交数据,大家做个参考. php发送post请求的三种方法,分别使 ...

  3. php发送get、post请求的6种方法代码示例

    本文主要展示了php发送get.post请求的6种方法的代码示例,分别为使用file_get_contents .fopen.fsockopen.curl来发送GET和POST请求,代码如下: 方法1 ...

  4. DataTable数据批量写入数据库三种方法比较

    DataTable数据批量写入数据库三种方法比较 标签: it 分类: C#1)   insert循环插入:2)   sqldataadapter.update(dataset,tablename); ...

  5. 【MySQL】锁——查看当前数据库锁请求的三种方法 20

    MySQL提供了查看当前数据库锁请求的三种方法:1. show  full  processlist命令  观察state和info列 2. show engine  innodb status\G ...

  6. 手机数据抓包的N种方法

    手机数据抓包的N种方法 - xia_xia的博客 - 博客频道 - CSDN.NEThttp://blog.csdn.net/xia_xia0919/article/details/50606137 ...

  7. DataTable 数据批量写入数据库三种方法比较

    DataTable数据批量写入数据库三种方法比较 1)   insert循环插入: 2)   sqldataadapter.update(dataset,tablename); 3)   sqlbul ...

  8. 使用命令行将Excel数据表导入Mysql中的方法小结

    从Excel数据表导入MySQL,已经做过好几次了,但每次都会碰到各种问题:invalid utf8 character string, data too long, ...,浪费了不少时间 为了提高 ...

  9. 【MySQL笔记】Excel数据导入Mysql数据库的实现方法——Navicat

    很多公司尤其有点年头的公司,财务业务部门的各种表单都是excel来做的表格,随着互联网的发展各种业务流程都电子化流程化了,再在茫茫多的文档中去查找某一个年份月份的报告是件相当枯燥的事,所以都在想办法将 ...

随机推荐

  1. 你不知道的JavaScript(六)Box&Unbox

    很多语言中都有Box和Unbox的概念,很多书籍把Box翻译为"装箱操作",指的是將基本数据类型包装成对象:Unbox和它相反,把对象类型转换为基本类型. 我们知道JavaScri ...

  2. (转载) Android-Spinner的使用以及两种适配器

    目录视图 摘要视图 订阅 赠书 | 异步2周年,技术图书免费选      程序员8月书讯      项目管理+代码托管+文档协作,开发更流畅 Android-Spinner的使用以及两种适配器 201 ...

  3. springmvc整合mybatis实现商品列表查询

    转载.https://blog.csdn.net/chizhuyuyu/article/details/82180404 https://www.jianshu.com/p/689bdd11bfcc. ...

  4. C语言中的作用域、链接属性与存储属性

    C语言中的作用域.链接属性与存储属性 一.作用域(scope) 代码块作用域 表示{}之间的区域,下例所示,a可以在不同的代码块里面定义. #include<stdio.h> int ma ...

  5. 定时清理clientmqueue目录垃圾文件防止占满磁盘空间

    RedHat/CentOS 5.8 默认就有sendmail,而6.4默认没有.   手动清理方法: find /var/spool/clientmqueue/ -type f|xargs rm -f ...

  6. Node_进阶_4

    Node进阶第四天 一.传统数据库技术回顾 数据库就是存储数据的,那么存储数据用txt就行了啊,为什么要有数据库? 理由之一:数据库有行.列的概念,数据有关系,数据不是散的. 老牌数据库,比如Mysq ...

  7. tinymce原装插件源码分析(二)-link

    link 功能描述如下: 单纯放置光标: 1.如果光标放到了<a>上,读取a标签的内容,并弹框显示,确定的时候,更新当前a标签. 2.否则,就创建弹框,确定的时候,按照参数添加a标签. s ...

  8. Linux学习02--Linux一切皆文件

    Linux学习第二部 Linux一切皆对象 啊啊啊啊啊,今天被学妹说太直了,嘤嘤嘤. 学习linux两三天了,前期感觉并不难,只是命令多,多记记多敲一敲就能都记住了.希望自己能够坚持下去吧! 下面是根 ...

  9. Docker学习总结(4)——Docker镜像与容器命令

    Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的.可移植的.自给自足的容器.开发者在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机).bare metal. ...

  10. 【Hibernate学习】 ——ORM(二)

    上篇博客主要介绍了一对一的关系,主要理解单向与双向的差别,主键关联与唯一外键关联的差别.以下继续介绍一对多与多对多关联. 一对多关联映射 一个班级相应多个学生 watermark/2/text/aHR ...