大报表高并发导出 Excel 的限流措施
大报表导出因数据量巨大,导出 EXCEL 文件时间长。高并发导出请求会同时启动过多的线程执行导出服务,由于单次执行时间长,服务完不成堆积起来,对 cpu 的竞争非常激烈,可能原本要一分钟执行完毕的任务,拖长到几十分钟,最后经过漫长等待所有用户几乎同时完成。所有用户都处于等待中,却缺少有效反馈,可能以为导出出问题了,而失去耐心。这时不如从服务器端控制,限流或排队。
而且目前没有对大报表并发导出 EXCEL 内存需求的明确指导。
因此做了以下测试,从中找规律以解决上述问题。(本文不讨论大报表导出时与数据库交互的开销)
测试过程简录放在文后,直接说结果:
一,关于大报表导出内存消耗
计算和测试结果依据 jvm 堆快照和 cpu 占用率分析:可容纳得总并发数与单页大小(格子为单位),和 jvm 最大内存大小有关。
大报表单元格实例大小较为稳定,约 107B(不包含图片时)。
报表页实例存在很大的矩阵对象,大小随格子数增加而近似线性增加,约合
(一页单元格内存大小)*0.25;
即总大小占用约为
(一页单元格数量 *107B)*1.25
= 一页单元格数量 *133.75B
= 一页单元格数量 /100000 * 12.76M (换算成兆)
单页 100000 单元格量,单次导出 jvm 至少需要 12.76M 空闲。
注意虚拟机自身和其他应用、线程对虚拟机内存同样有消耗,在较为空闲的 jre1.8 环境启动虚拟机做上述 [单次][100000/ 页] 导出至少还需要额外的 200M(不考虑减少 GC 次数得需求)。
并发累计次数 *12.76M* 一页单元格数 /100000 = JVM 空闲空间
举例:
若控制每页显示 10000 数据量(例如 500 行 *20 列),虚拟机大小为 500M,不考虑其他应用,则支持最大并发数:
(100000/10000)*(500-200)/12.76 = 约 234 次并发
二,关于 cpu 工况与并发数量的关系
cpu 占用率随着导出线程增加而增加,当并发数量到达一定高度时候,若 JVM 内存紧张,GC 比例会开始增加,增加时间开销,也应据此调整 JVM 大小。
过少的并发是性能浪费。环境单机,本地,锐龙 R7,8 核 16 线程 cpu,经过测试,上述单页 10000 格数据,总数据量 120W 的大报表,cpu 占用会在 10-15 并发达到峰值;并发数量大于 10 时平均每个文件需要的导出时间最低。
当并发数量大于 10(平均每个文件需要的导出时间来到最低)时,导出总时长虽然随着并发数量增加而增加,但平均每个文件导出时长没有很大变化。
综上,应用并发编程的思想,若要提高系统性能,应结合自身工况:
1,合理设置 JVM 空间
2,分布式部署报表缓解单机压力
3,结合工况对大报表导出服务限流
4,总导出时间相近,单个文件导出时间相近情况下,一部分客户若可以先获得结果,体验会更好一些(增加排队机制)
三,解决方案
对于 1,2 客户可以自行调整。对于 3,4 现在已增加如下措施:
报表应用增加针对大报表导出的限流、排队机制配置。这是两种不同的方式。
1,如果要使用直接的大报表导出限流,可以在报表应用配置文件中增加 bigReportExcelExportLimit 属性:
<Server>
<property name="bigReportExcelExportLimit" value="10"/>
……
</Server>
此时,大报表导出功能被限流,在此应用上,任意用户导出任意大报表均会进入计数,当数量到达设置的 10 的时候,后续用户请求导出会直接被拒绝并返回,并且弹框提示:【大报表导出队列受限,请稍后再试】。直到某(几)个用户完成了导出,数量被释放,其他用户此时再请求才可以进入正常导出大报表的状态。
2,排队机制目前处于试用,如果要使用排队机制,可以在报表应用配置文件中增加以下属性:
<Server>
<property name="bigReportExcelExportDirectory" value="D:/testQueueDir"/>
<property name="bigReportExcelExportQueueCapacity" value="100"/>
<property name="bigReportExcelExportExecutorThreads" value="10"/>
<!-- 0下载后不保留文件,1保留文件30分钟 -->
<property name="bigReportExcelExportFileManagement" value="0"/>
<property name="bigReportExcelExportTempCleanInterval" value="30000"/>
……
</Server>
1) 这里需对此机制进行解释,以理解上述配置概念:
排队导出 EXCEL 的队列基于并发安全的阻塞队列开发。队列有一定容量,到达容量最大值后,后续大报表导出 EXCEL 请求被拒绝,同上面限流机制。队列中的请求被设置好的 n 个处理器处理,既并发处理数量。由于客户不会快速即时地获得导出结果,而是获得排队、导出状态,文件实际被导出到硬盘,供客户下载,下载后即删。
因此我们需要配置:
bigReportExcelExportDirectory---- 大报表 Excel 临时文件导出到硬盘的位置,绝对路径
bigReportExcelExportDirectory---- 队列容量,可大可小
bigReportExcelExportExecutorThreads---- 并发数量,比如我们测试结果 10 次并发合理
bigReportExcelExportFileManagement---- 导出后文件管理策略
bigReportExcelExportTempCleanInterval---- 策略 1 时间间隔
2) 其中,【导出后文件管理策略】一向目前有两个值可选 0 代表硬盘临时文件被下载后立即删除;1 代表硬盘临时文件从生成完成开始 30 分钟超时,才会被删除。而策略 1 下的文件超时检查时间间隔由【策略 1 时间间隔】决定。注意:策略 1 可方便单个页面反复下载,但同时会更多地占用硬盘空间(一定时间才会清理一次),而且临时 Excel 文件也都很大,要谨慎使用。
3) 上述配置需要再报表应用启动前配置好,排队机制的可用以 bigReportExcelExportDirectory 的正确配置为标志。
4)以上配置正确,仅标志排队机制可用。至于使用它,我们需要通过前端 javascript 函数入口 function queueExcel(); 来请求导出 EXCEL。用户自定义某个事件(如:自定义图标点击)调用此函数即可。
5) 效果
以下是测试过程的一些简录,使用 visualVM 监控:
5 并发耗时 129s
10 并发耗时 189s
15 并发 GC 0.0-1.0%,耗时 234s
20 并发耗时 312s
30 并发耗时 465s
以下省略部分图片
50 并发耗时 794s
100 并发 GC 0.7-1.9% 耗时 1537s
200 并发
运行 20 分钟后
内存占用计算较为可靠。另由于时间严重延长,GC 占掉其中 3% 的时间,考虑网络链接超时变得不可靠、系统性能大幅下降,建议如下:
1,根据实际情况,分析堆空间,适当增加 JVM 内存,调优 JVM GC 策略
2,限制导出业务层并发数量,目前提供了 bigReportExcelExportLimit 报表应用参数,可硬性地限制并发数量,拒绝超限制导出请求
3,集群部署报表应用,以减轻单机负担
4,Tomcat 超时设置为 0
5,虚拟机设置 HeapFreeRatio、NewSize、NewRatio、SurvivorRatio、MaxPermSize 等改变 GC 空间比例,适当减少回收时机以提高 CPU 性能
6,线程增多可能导致大量任务不能完成处于等待,也可能导致内存短时间内占用增加
7,检查其他应用或报表其他线程内存占用情况
附:200 次并发测试 JVM 配置了 -XX:+HeapDumpOnOutOfMemoryError,过程中未发生 OOM 异常。
大量线程同时工作抢占 CPU,停顿时间长
导出结果正常
导出结束
回收后空间富余
对润乾产品感兴趣的小伙伴,一定要知道软件还能这样卖哟性价比还不过瘾? 欢迎加入好多乾计划。
这里可以低价购买软件产品,让已经亲民的价格更加便宜!
这里可以销售产品获取佣金,赚满钱包成为土豪不再是梦!
这里还可以推荐分享抢红包,每次都是好几块钱的巨款哟!
来吧,现在就加入,拿起手机扫码,开始乾包之旅
嗯,还不太了解好多乾?