PDF批量打包下載
需求
產(chǎn)品要求每個稅盤每月支持最大1萬張PDF的打包下載
思路
每月1號定時往pdf_batch表插入一條記錄(也可以做成用戶實時篩選的模式);
另外每10秒啟動一次輪詢?nèi)蝿?wù)存捺,遍歷下載PDF到特定磁盤路徑(batchId/diskName/storeId/xxx.pdf)下藤巢。
借助shell打包磁盤目錄蔫磨,刪除磁盤文件夾祝拯,上傳oss成功后您没,再刪除zip包厂僧。
對于失敗的PDF扣草,要把錯誤原因打包到zip包中。
異常PDF前端會給用戶展示颜屠,并提供補充的入口辰妙。
注:該功能為次要功能,由于大量占用帶寬和CPU(壓縮)甫窟,所以不能放到核心系統(tǒng)密浑,應(yīng)該放到非核心系統(tǒng)(eg: 運營系統(tǒng))。
表設(shè)計
pdf_batch(批次表):
id, diskId, month, type, status, total_size, ip, begin_time, end_time,
save_point, err_msg, zip_path, priority, retry_times, create_time, update_time
索引:
uni_key1(diskId, month)
key2(status, ip, priority, create_time)
說明:
type:1-正常的PDF下載粗井,2-異常的PDF補充下載
status:1-待處理尔破,2-處理中(會重試)街图,3-處理成功,4-異常中斷(會重試)懒构,5-永久中斷(不重試)
pdf_batch_err(每個批次內(nèi)部的PDF異常列表):
pdf_batch_id, invoice_id, pdf_path, err_msg
核心點
高性能高可用
多臺機器同時跑:防止單點故障餐济,并提高性能
每臺機器最多有2個線程參與:防止該功能影響運營項目其他業(yè)務(wù)
pdf_batch表會記錄ip,限制每個ip最多只有2個status=2的任務(wù):如果沒有ip限制痴脾,總體限制4個status=2的任務(wù)的話颤介,一旦某臺機器宕機,就會讓剩余機器壓力倍增赞赖,排隊慢慢處理才是正道滚朵;而且也容易導(dǎo)致每臺機器的處理線程數(shù)不均勻。
磁盤刪除和淘汰策略
每個任務(wù)執(zhí)行后前域,都會對dir和zip做刪除辕近。
中斷(異常中斷/永久中斷)的任務(wù),dir不刪除匿垄。
每個batch任務(wù)執(zhí)行前移宅,都有diskFree監(jiān)測,如果空間小于10G(兩個任務(wù)同時跑最多占2G椿疗,留足余量防止意外):1. 會list當(dāng)前目錄下所有的batchId文件夾和zip包漏峰,如果有處理成功但刪除失敗的文件(夾),做二次刪除届榄;2. 如果有的dir=batchId當(dāng)前執(zhí)行的ip不是本機浅乔,說明任務(wù)被其他機器搶走,放心的做本地刪除铝条;3. 如果都是status=2/4/5的臨時文件靖苇,則淘汰最早的文件夾,并對本機ip執(zhí)行的batch任務(wù)savePoint做重置班缰。
自動重試
status=2的場景:重啟贤壁、宕機、網(wǎng)絡(luò)分區(qū)埠忘、網(wǎng)絡(luò)特別慢(oss下載超級慢) => 本機重試
status=4的場景:一般是數(shù)據(jù)不一致導(dǎo)致(disk數(shù)據(jù)丟失=>重試沒用)脾拆,打包異常(權(quán)限/磁盤滿了/打包超時=>其他機器可以重試),oss上傳失敗(可以重試)莹妒,打包前后校驗失敗(可以重試)假丧,文件創(chuàng)建/刪除失敗等(其他機器可以重試)
注:多個線程同時下載同一個文件,到本地相同的位置动羽,是不會報錯的包帚,文件會不斷被覆蓋。所以不用擔(dān)心網(wǎng)絡(luò)慢導(dǎo)致超時后运吓,兩個線程同時下載文件會有沖突而拋異常渴邦。場景聯(lián)想:A線程下載pdfX后更新savePoint=1001疯趟,然后B線程下載pdfX后更新savePoint=1001,正常場景是OK的
pageQuery和savePoint
每次對PDF的查詢都是page(100)谋梭,每成功下載一個PDF信峻,都去更新一次savePoint。
pdf數(shù)量+err數(shù)量=totalSize
保證:打包后zip包中PDF的數(shù)量 + pdf_batch_err的數(shù)量 = totalSize
err.txt打包到稅盤同級目錄瓮床,展示給用戶每個失敗PDF的錯誤原因盹舞。
打包前后校驗
檢驗dir在打包前后的修改時間
PDF數(shù)量
目錄大小
問題
機器重啟(短暫):任務(wù)線程會中斷,status一直為2隘庄,只能等待超時
宕機(時間可能很長):任務(wù)線程會中斷踢步,status一直為2,只能等待超時
網(wǎng)絡(luò)長時間分區(qū)(機器和DB不通):任務(wù)線程不中斷丑掺,但連接數(shù)據(jù)庫報錯获印,status一直為2。
線程異常:前置大量校驗街州,try-catch捕獲 => status變?yōu)?/5 [什么錯誤可以重試兼丰,什么錯誤沒必要重試:可以預(yù)先想一批,發(fā)到線上不斷觀察和完善]唆缴。
執(zhí)行流程
10s定時任務(wù)啟動時鳍征,查詢"status=2 and ip=自己"的記錄list;
如果有超時面徽,報警并對它做重試:重試時艳丛,從savePoint開始;
沒有超時斗忌,則判斷有幾條記錄质礼,如果大于等于2旺聚,則任務(wù)結(jié)束织阳;如果小于2,則當(dāng)前線程加入砰粹;
展望
針對特定status=4的場景做重試:可能本機重試唧躲,也可能交給其他機器重試
每次更新savePoint時,如果已下載數(shù)量超過10碱璃,計算平均速率弄痹,預(yù)估剩余執(zhí)行時間,如果特別慢嵌器,則本機任務(wù)stop肛真,交由其他機器嘗試執(zhí)行。
發(fā)票抽取
描述:將用戶稅盤和本地數(shù)據(jù)庫的發(fā)票抽取到業(yè)務(wù)系統(tǒng)爽航。
實現(xiàn):
1.客戶端進程會定時(5min)解析發(fā)票數(shù)據(jù)(幾M ~ 幾十M的小包)蚓让,上傳oss乾忱,oss監(jiān)控meta.json文件觸發(fā)postObject事件,然后產(chǎn)生MNS消息历极;
2.獨立的抽取項目會訂閱MNS消息隊列窄瘟,基于本地數(shù)據(jù)庫做數(shù)據(jù)校驗、過濾趟卸、格式轉(zhuǎn)換等操作蹄葱,然后將最終的發(fā)票數(shù)據(jù)發(fā)送給開票系統(tǒng);
3.開票系統(tǒng)做發(fā)票落庫和展示锄列。
數(shù)據(jù)遷移
第一階段:
只寫老庫
只讀老庫
第二階段:
主寫老庫,次寫新庫. 新庫異常走補償重試.
只讀老庫
第三階段:
主寫老庫,次寫新庫,同上階段
只讀新庫(當(dāng)然也可以走灰度慢慢放量至讀新庫)
第四階段:
在上述流程穩(wěn)定一段時間后.
主寫新庫,次寫老庫,寫老庫異常走補償 (此步可一步到位,直接主寫新庫,不寫老庫)
只讀新庫