MapReduce思路生成PDF大文件解决方案

最近一段时间公司搞个项目,其中有个将数据按照一定的格式生成PDF文件的功能。在网上搜寻以后决定采用itextpdf插件来实现生成PDF的功能。最初的生成思路比较老套,简单四句话,将要输出的格式做成html,在程序中读取html文件,从数据库获取数据替换html文件中的替换符,用itextpdf将html转换成pdf后保存到硬盘在数据库保存PDF路径。

功能完成后经过QA同事的测试上线了。但是上线后发现总有那么几个用户的文件生成不了,总是报内存溢出错我。经过线上排查发现,这几个用户的数据量都在万条以上,有个用户甚至超过了10万条,这个规模的数据一次从数据库取出来,然后生成PDF,不内存溢出才怪呢。

针对这个问题,又开始了网上搜寻。网上有的说专门找台机器,给这个机器足够大的内存,起一个JVM来生成PDF。这种方式从硬件角度来解决,但是这样会增加硬件成本,而且针对这个问题找OPS估计他们也不会过,没办法只能从软件角度来解决。

在之前的文章中分享过 。文章中针对海量超过内存的数据处理提供了几种思路,哈希切分、位图、布隆过滤、哈希表、堆等。乍一看有点懵,不过仔细分析后发现虽然方式不同,但是基本思路一样,就是将大数据切分成小的数据,然后一个一个处理,最后再针对各个小数据的结果进行处理,也就是大数据处理的MapReduce思路。

看到这里,举一反三,那么生成PDF是否也可以这样呢。比如:先生成小的PDF,每个小PDF 500条数据,等生成完后再把生成的小PDF集合成一个大的PDF,这样就可以从开发角度来解决生成PDF内存溢出的问题。

好了,思路已经理清,接下来我们来看具体实现。

Pom.xml中引入itextpdf包

MapReduce思路生成PDF大文件解决方案

实现字符串生成PDF的方法:

MapReduce思路生成PDF大文件解决方案

分批取出数据生成单个的PDF,记录生成的PDF路径。

分批取数据的实现思路:以一个表的不重复字段为条件,设置这个字段的最小值,分页从数据库查询记录,查询出来后循环数据的时候看这个字段的记录值是否大于当前值,如果大于则用记录值替换当前值。循环结束后再次查询数据库,直到数据库查询不出数据为止。具体代码如下(代码有些长,不用特别在意实现细节,每个公司的业务都不一样,关注实现思路即可):

MapReduce思路生成PDF大文件解决方案

MapReduce思路生成PDF大文件解决方案

MapReduce思路生成PDF大文件解决方案

MapReduce思路生成PDF大文件解决方案

MapReduce思路生成PDF大文件解决方案

MapReduce思路生成PDF大文件解决方案

MapReduce思路生成PDF大文件解决方案

合并生成的小PDF为一个大的PDF文件:

MapReduce思路生成PDF大文件解决方案

合并完成后将大PDF的路径保存到数据库,小的PDF从硬盘删除即可。

这种实现方案解决了生成PDF内存溢出的问题,但是还有两个问题没有解决。

  • 生成文件的时间过长,如果是同步的话那么链接早就断开,会影响其它功能的正常使用。

  • 对硬盘IO带来一定压力。

针对第一个问题可以采用将同步变异步的方式来解决,每个公司的异步实现方案可能所有不同,一般使用消息队列或者线程池。使用线程池的话需要注意线程安全和IO问题。

对硬盘IO带来的压力,目前来看可以忽略。

MapReduce思路生成PDF大文件解决方案

MapReduce思路生成PDF大文件解决方案

MapReduce思路生成PDF大文件解决方案


分享到:


相關文章: