溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

scala系統(tǒng)導(dǎo)出大文件問題解決方案

發(fā)布時間:2020-08-06 22:26:04 來源:網(wǎng)絡(luò) 閱讀:1179 作者:金明略 欄目:開發(fā)技術(shù)

     這周遇到了一個技術(shù)難題,我們系統(tǒng)中的導(dǎo)出文件功能雖然早已完成,但是當(dāng)導(dǎo)出超過8MB的文件時就會提示GC overhead,一開始只把問題定位到緩沖區(qū)不夠,所以試圖找到一條分批查詢的方案,經(jīng)過一定時間的調(diào)研就找到了一個看似挺好的方案:

protected void fillHeader(String[] columns, int contentLen, int headNums) {

    tmpwb = new XSSFWorkbook();

    tmpwb = updateDimensionRef(tmpwb, columns.length - 1, contentLen + headNums);

    wb = new SXSSFWorkbook(tmpwb, DEFAULT_ROW_ACCESS_WINDOW_SIZE);

    sh = wb.getSheet(DEFAULT_SHEET_NAME);

    sh.setRandomAccessWindowSize(DEFAULT_ROW_ACCESS_WINDOW_SIZE);

}

    這一方案中,EFAULT_ROW_ACCESS_WINDOW_SIZE指的是緩沖區(qū)中存放的行數(shù),超過這個行數(shù)就會把文件溢寫到磁盤里,這樣就起到了動態(tài)釋放緩沖區(qū)的作用,由于原先用的是

XSSFWorkbook,而這一方案需要使用SXSSFWorkbook,而SXSSFWorkbook中沒有更新dimension的方法,所以先在XSSFWorkbook中定義好dimension,然后再轉(zhuǎn)換成SXSSFWorkbook.經(jīng)過這次改動后試驗發(fā)現(xiàn),性能確實有所提升,導(dǎo)出15MB左右不成問題.

    本來以為大功告成,可是第二天測試MM再次給我報bug,說導(dǎo)出70MB的大文件仍然報錯,而客戶需求的最大文件是80MB,我突然意識到問題沒有那么簡單,革命尚未成功,同志仍需努力.

    我開始研究報的錯,Error java.lang.OutOfMemoryError: GC overhead limit exceeded,在StackOverflow上搜了一些帖子,建議調(diào)整jvm參數(shù),于是我把-Xmx和-Xms都調(diào)成了4g,結(jié)果發(fā)現(xiàn)這個錯誤消除了,但又開始報新的錯誤,504 gateway-timeout,根據(jù)上面的提示,結(jié)合Google的帖子,我認為問題可能是出在導(dǎo)出數(shù)據(jù)量太大,導(dǎo)致需要時間長,而我們的超時時間可能設(shè)置得太短,覺得有必要調(diào)整一下nginx的參數(shù),于是修改了一下nginx.conf中的配置,把proxy_send_timeout和proxy_read_timeout的值都調(diào)成了600,延長了nginx后端服務(wù)器數(shù)據(jù)回傳時間和等候后端服務(wù)器響應(yīng)時間,由于和系統(tǒng)連接時間無關(guān),因此proxy_connect_timeout 這個參數(shù)就不更改了.經(jīng)過這次更改之后,發(fā)現(xiàn)依然會報504 gateway-timeout,這時候意識到可能問題并不是單方面的,開始思考解決方案.

    現(xiàn)在的重點就是要找出導(dǎo)致導(dǎo)出失敗的根源,于是在調(diào)用這個接口的一系列相關(guān)代碼,從routes開始,幾個關(guān)鍵點都打上log,最后發(fā)現(xiàn)在在一個Handler的最后一步還能打印出日志,說明那個Handler已經(jīng)執(zhí)行完畢,而從Handler發(fā)送消息的那個接收方Actor收到結(jié)果后做的第一件事情就是打印一條日志,但是那一條一直沒有打印出來,那么問題就定位到了Actor傳送消息的過程,一開始只想到可能是Akka消息傳遞過程的超時,于是延長了系統(tǒng)中每一個接口的超時時間,之后沒有再報接口的Ask TimeOut,但是仍然有504 gateway-timeout,由于涉及到akka通信機制,所以我很自然地聯(lián)想到akka的參數(shù)是否可以設(shè)置一下,由于我們傳輸?shù)氖侨聿樵兘Y(jié)果,量非常大,因此想到是否把容量類參數(shù)的值設(shè)置得大一點,maximum-frame-size這個參數(shù)表示最大允許傳輸?shù)臄?shù)據(jù)量,另外send-buffer-size和receive-buffer-size這兩個參數(shù)也是限制消息傳遞大小的,于是把這三個參數(shù)都調(diào)大,基本上問題定位到了消息傳遞的大小限制,因為我認為超時時間已經(jīng)設(shè)置得很長了,按照傳遞消息的速度來看,不至于這么久還沒傳完,因此唯一的可能性是本身就不允許傳遞大數(shù)據(jù)量,而這些一般都可以在參數(shù)中設(shè)置,這么一想,我滿以為問題終于快得到解決了.

    令人遺憾的是,問題并沒有因為調(diào)參而解決了,后來我一度還嘗試了把maximum-payload-bytes這個參數(shù)的值也調(diào)大,發(fā)現(xiàn)依然無濟于事,似乎又陷入了迷茫當(dāng)中.但是我依然沒有放棄尋找答案,這個時候發(fā)現(xiàn)一篇帖子:http://stackoverflow.com/questions/31038115/akka-net-sending-huge-messages-maximum-frame-size ,上面那個程序員也遇到了類似的問題,根據(jù)他的描述,我推斷出可能akka消息傳輸是存在一個上限的,大于這個上限的話設(shè)置的參數(shù)也會是無效的,但是這一點我還沒有去證明,如果要證明這些,需要研究一下akka的源碼,愿各位大神們有空做個分享,好,就是你啦~~.回到那個問題,那個帖子的樓主這么描述:Also, if the message is being dropped, is there any way to get notified about it? Sometimes it sends a Dissassociated error and sometimes it just sits doing nothing. log-frame-size-exceeding = on and log-buffer-size-exceeding = 50000 settings do not seem to have an effect.

    這個時候,下面有一個無名神僧道出了天機,讓我恍然大悟,他說:In general it's a bad idea to push big portions of data over the wire at once. It's a lot better to split them into smaller parts and send one by one (this also makes a retry policy less expensive, if it's necessary). If you want to keep your actor logic unaware of transport details, you may abstract it by defining a specialized pair of actors, whose only job will be to split/join big messages.

    我突然意識到,akka設(shè)計者的初衷可能只是想通過actor來傳送消息,并沒有讓你們通過這種傳送消息的機制直接傳送大批量的數(shù)據(jù),這種全表查詢結(jié)果寫入到大文件并且傳送的問題還是更適合使用scp來傳送,或者用rz,sz也可以實現(xiàn)文件的收發(fā),或者用一個python的插件也可以實現(xiàn)在網(wǎng)頁上的秒傳,其中scp和那個我記不清名字的python插件(工作文檔有記錄)是我用過的體驗比較好的兩個文件傳輸工具,可以輕松傳輸hive和HBase等導(dǎo)出的大批量數(shù)據(jù),而Akka或許目前的設(shè)計只是用來傳遞短信息而已,就像一條微博只能輸入140個字,你要表達一大段內(nèi)容,只能使用博客和日志等功能,本身面向的用途和使用場景就是不一樣的.

    數(shù)據(jù)切分,用多線程來解決問題,也是一個思路,同事建議我這么做,但我發(fā)現(xiàn)多線程存在同步的問題,讀取的順序必須完全保持一致,我們的產(chǎn)品下周就要交付了,同時測試MM如果要再抽出額外時間測大量多線程問題,大家壓力都會比較大,而且留給我們的時間也不多了.當(dāng)然最主要的還是我考慮到目前文件也不算特別大,能傳輸100MB已經(jīng)可以滿足客戶需求,用多線程會消耗更多系統(tǒng)字段,而且創(chuàng)建每個線程,以及對線程的管理也消耗時間,綜合考慮,覺得暫時沒有必要使用多線程.既然問題卡在Actor傳送消息這塊,那么我是否可以把處理邏輯都放在一個Handler上,包括全表查詢,查詢結(jié)果寫入Excel以及Excel的導(dǎo)出,這一系列的流程都在一個Handler里搞定,最后只給之前的那個Actor發(fā)送是否導(dǎo)出成功的消息,同時回傳下載的路徑,在那個Actor里,只需要做下載這一步就可以了.說干就干,昨天下午把這塊代碼改成了我想要的樣子,改完之后在本地做了測試,目前導(dǎo)出100MB的文件已經(jīng)不成問題,時間也就3分鐘,最多4分鐘,如果有一個進度條,用戶應(yīng)該是很容易接受這樣的速度的,畢竟平時我們用其他系統(tǒng)導(dǎo)出文件也都需要時間,至此,這個問題算是告一段落了.

    這次解決問題的過程,從找到一個方案,由于導(dǎo)出更大文件還是不行而否定,再到后來定位問題,定位問題后發(fā)現(xiàn)這個方案行不通,最后換方案,試驗成功,我發(fā)現(xiàn)自己還有許多時間可以優(yōu)化,主要是要縮短試錯的成本,要在較短的時間內(nèi)能夠發(fā)現(xiàn)一條路走不通,然后換一條路.路漫漫其修遠兮,吾將上下而求索.


向AI問一下細節(jié)
推薦閱讀:
  1. scala 介紹
  2. scala

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI