• 极速响应Excel数据报表请求的一种方法


    摘要

    通过缓存和维护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)

  • 相关阅读:
    JAVA核心技术笔记总结--第14章 线程总结
    java核心技术笔记——第 9 章 集合
    Java核心技术笔记——第 8 章 泛型
    2.面向对象三大特征
    1.浅谈面向对象思想
    8.字符串
    7.数组
    6.调试程序
    5.流程控制语句
    4.运算符
  • 原文地址:https://www.cnblogs.com/qitian1/p/6463517.html
Copyright © 2020-2023  润新知