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大文件解決方案


分享到:


相關文章: