戰(zhàn):Kettle作業(yè)錯(cuò)誤流與日志表設(shè)計(jì)指南)
做ETL的老司機(jī)應(yīng)該都經(jīng)歷過這種場(chǎng)景半夜兩點(diǎn)的告警電話爬起來打開Webspoon看日志折騰半小時(shí)才發(fā)現(xiàn)是源頭接口超時(shí)但任務(wù)已經(jīng)重跑了好幾遍臟數(shù)據(jù)早就進(jìn)了目標(biāo)表。這個(gè)場(chǎng)景之所以常見是因?yàn)榇蠖鄶?shù)人在寫Kettle作業(yè)時(shí)根本沒把“出錯(cuò)了怎么辦”當(dāng)回事——默認(rèn)不配置錯(cuò)誤處理轉(zhuǎn)換一報(bào)錯(cuò)整個(gè)作業(yè)就停停完就算完沒人知道為什么停的更沒人知道哪些數(shù)據(jù)在哪個(gè)環(huán)節(jié)被丟棄了。這一課我們就來啃這塊硬骨頭在Webspoon里做全局異常/錯(cuò)誤捕獲。Webspoon是Kettle的Web化版本直接在瀏覽器里設(shè)計(jì)作業(yè)和轉(zhuǎn)換部署在服務(wù)器上后不用裝客戶端就能跑批。但正因?yàn)槭荳eb版很多人在做異常捕獲時(shí)比在本地客戶端里更懵——界面布局不一樣日志查看不如桌面端直觀錯(cuò)誤處理配置找不到入口。這篇內(nèi)容會(huì)從錯(cuò)誤捕獲的核心思路講起再拆解具體的配置步驟和排錯(cuò)技巧適合已經(jīng)把Kettle基礎(chǔ)玩明白、正在往生產(chǎn)環(huán)境級(jí)別進(jìn)階的同學(xué)參考。1. 為什么ETL需要全局異常捕獲異常到底去哪兒了1.1 ETL任務(wù)報(bào)錯(cuò)的三層典型形態(tài)先說一個(gè)觀點(diǎn)ETL里的“異常”不是一個(gè)東西而是三個(gè)層面的東西。搞清楚這三層后面的捕獲設(shè)計(jì)才有方向。第一層是作業(yè)執(zhí)行層面的異常。比如數(shù)據(jù)庫連接失敗、目標(biāo)表不存在、權(quán)限不足這些錯(cuò)誤發(fā)生在作業(yè)調(diào)度的層面往往整個(gè)轉(zhuǎn)換都起不來。這類異常的特點(diǎn)是報(bào)錯(cuò)信息直白日志里通常有Caused by開頭的堆棧你一眼就能看出是連接字符串寫錯(cuò)了還是驅(qū)動(dòng)沒放進(jìn)去。在Webspoon里這類錯(cuò)誤的表現(xiàn)通常是作業(yè)節(jié)點(diǎn)變紅點(diǎn)擊節(jié)點(diǎn)能看到詳細(xì)的異常堆棧。第二層是數(shù)據(jù)轉(zhuǎn)換層面的異常。這是最常見的也是最容易靜默丟失的。比如源表某個(gè)字段本來應(yīng)該是數(shù)字結(jié)果源庫里混入了幾個(gè)字母轉(zhuǎn)換里的“字段選擇”或“字符串轉(zhuǎn)數(shù)字”步驟直接報(bào)錯(cuò)又比如目標(biāo)表字段長度是50源數(shù)據(jù)某條記錄塞了80個(gè)字符進(jìn)來寫入時(shí)被數(shù)據(jù)庫拒掉。這類異常的特點(diǎn)是不穩(wěn)定、跟數(shù)據(jù)有關(guān)可能這一次跑批不報(bào)錯(cuò)下一次就冒出來了完全取決于源端數(shù)據(jù)質(zhì)量。第三層是業(yè)務(wù)規(guī)則層面的異常。這層最隱蔽因?yàn)镵ettle不會(huì)報(bào)錯(cuò)甚至不會(huì)提示。比如你從接口里取到一個(gè)JSON字段解析后發(fā)現(xiàn)里面的金額是負(fù)數(shù)但業(yè)務(wù)上不允許負(fù)數(shù)再比如實(shí)時(shí)流里有重復(fù)主鍵你的“去重”步驟設(shè)置不當(dāng)這些記錄就多寫了一次。這類異常Kettle是“無感”的它正常跑完了結(jié)果卻是錯(cuò)的。我們說的“全局異常/錯(cuò)誤捕獲”至少要覆蓋前兩層第三層需要借助數(shù)據(jù)校驗(yàn)和血緣分析來兜底。這一課的重點(diǎn)會(huì)放在第一和第二層也就是怎么讓任務(wù)報(bào)錯(cuò)時(shí)能被接住、被記錄、被看見。1.2 沒有錯(cuò)誤捕獲的典型事故鏈為什么很多Kettle任務(wù)在生產(chǎn)環(huán)境跑了一個(gè)月都沒事突然某天就“死了”關(guān)鍵在于ETL是個(gè)鏈?zhǔn)浇Y(jié)構(gòu)每一步都可能出錯(cuò)但默認(rèn)配置下Kettle的處理方式是“一錯(cuò)全?!鞭D(zhuǎn)換里某個(gè)步驟報(bào)錯(cuò)行集直接斷掉后面的步驟拿不到數(shù)據(jù)整個(gè)轉(zhuǎn)換停止作業(yè)標(biāo)記失敗。聽起來好像是合理的嚴(yán)謹(jǐn)嘛。但真正的坑在于任務(wù)停了但沒人知道停在哪一步、為什么停、丟了多少數(shù)據(jù)。Webspoon的作業(yè)運(yùn)行日志存放在內(nèi)存和日志表里如果不配置日志表刷新一下頁面日志就沒了你想復(fù)盤都沒得復(fù)盤。我親眼見過的一個(gè)案例某公司的日常訂單同步任務(wù)源庫有個(gè)字段叫order_status以前都是英文枚舉值某天源端業(yè)務(wù)改造后往里寫了一個(gè)中文值“已取消”。Kettle轉(zhuǎn)換里的“字符串替換”步驟沒匹配到這種值直接把整行數(shù)據(jù)置成NULL后面的寫入步驟遇到NULL就報(bào)主鍵沖突作業(yè)失敗。但因?yàn)闆]有錯(cuò)誤處理作業(yè)在日志里只留下“Failed to execute run: Unable to write to database”這么一句具體是哪條數(shù)據(jù)導(dǎo)致的完全不知道。最后是人工查目標(biāo)表、對(duì)比源表翻了半天才定位到那條臟數(shù)據(jù)。這個(gè)案例說明沒有全局異常捕獲你每天就是在一堆“跑批失敗”郵件里猜謎猜錯(cuò)了重跑可能還會(huì)產(chǎn)生重復(fù)數(shù)據(jù)。有了捕獲機(jī)制作業(yè)報(bào)錯(cuò)時(shí)能把錯(cuò)誤記錄、錯(cuò)誤數(shù)據(jù)、錯(cuò)誤原因全留下來你只需要看一張錯(cuò)誤日志表問題就清晰了。2. Webspoon里的錯(cuò)誤流設(shè)計(jì)從單步驟錯(cuò)誤到全局鏈路2.1 轉(zhuǎn)換內(nèi)的錯(cuò)誤處理定義聊錯(cuò)誤捕獲第一個(gè)要掌握的概念叫錯(cuò)誤流。Kettle的轉(zhuǎn)換Transformation里每個(gè)步驟都有輸入流和輸出流默認(rèn)情況下只輸出正常數(shù)據(jù)。但如果你在步驟上右鍵選擇“定義錯(cuò)誤處理”這個(gè)步驟就會(huì)多出一條錯(cuò)誤輸出流專門承載處理失敗的記錄。Webspoon里也一樣操作路徑是在步驟上點(diǎn)擊右鍵往下找“錯(cuò)誤處理”子菜單。這里需要說明的是Webspoon的右鍵菜單在不同版本里位置略有差異有的是“錯(cuò)誤處理”直接展開有的要先進(jìn)入步驟屬性頁的“錯(cuò)誤處理”標(biāo)簽頁需要你點(diǎn)進(jìn)步驟配置界面去找。錯(cuò)誤處理的定義里主要有幾個(gè)配置項(xiàng)目標(biāo)步驟錯(cuò)誤數(shù)據(jù)要流向哪個(gè)步驟通常是寫日志表、寫文件或者繼續(xù)作后續(xù)的清洗嘗試。錯(cuò)誤字段名比如error_desc、error_code這些字段會(huì)被附加到錯(cuò)誤流上每行錯(cuò)誤數(shù)據(jù)會(huì)帶上這個(gè)步驟拋出的錯(cuò)誤描述和錯(cuò)誤代碼。錯(cuò)誤數(shù)量上限當(dāng)錯(cuò)誤行數(shù)超過這個(gè)值步驟直接失敗停止。這個(gè)設(shè)計(jì)很實(shí)用比如你允許5行臟數(shù)據(jù)跳過不處理但超過5行說明源系統(tǒng)有大批量臟數(shù)據(jù)寧可停下來告警。注意一點(diǎn)錯(cuò)誤處理不是所有步驟都有。像“查詢”、“流查詢”這類步驟的錯(cuò)誤處理配置方式略特殊因?yàn)樗鼈兊腻e(cuò)誤不發(fā)生在行處理中更可能在“數(shù)據(jù)行不可用”的情況下觸發(fā)。而像“字段選擇”、“字符串操作”、“表輸入”、“文本文件輸入”這類行處理型步驟是定義錯(cuò)誤流最順手的。2.2 作業(yè)級(jí)的錯(cuò)誤分支設(shè)計(jì)如果只在轉(zhuǎn)換層級(jí)配錯(cuò)誤流你捕獲到的還是“局部錯(cuò)誤”——某一行的數(shù)據(jù)錯(cuò)誤不能覆蓋“整個(gè)轉(zhuǎn)換掛了”“資源連接失敗”這類全局問題。要捕獲這種級(jí)別的異常得在作業(yè)Job層級(jí)做設(shè)計(jì)。作業(yè)是一個(gè)由多個(gè)作業(yè)項(xiàng)Job Entry組成的有向圖每個(gè)作業(yè)項(xiàng)執(zhí)行完之后有三種結(jié)果成功、失敗、跳過。默認(rèn)情況下一個(gè)作業(yè)項(xiàng)失敗后整條鏈路往下繼續(xù)跑還是會(huì)停止取決于作業(yè)項(xiàng)之間的連線類型。這里有個(gè)非常關(guān)鍵的概念——連線上的結(jié)果約束。在Webspoon作業(yè)里你把鼠標(biāo)放在兩個(gè)作業(yè)項(xiàng)之間的連線上可以設(shè)置當(dāng)上一個(gè)作業(yè)項(xiàng)成功時(shí)走這條線當(dāng)上一個(gè)作業(yè)項(xiàng)失敗時(shí)走這條線無論成功失敗都走這條線很多人設(shè)計(jì)作業(yè)時(shí)只用了默認(rèn)的“成功”線結(jié)果就是作業(yè)項(xiàng)一旦失敗后面的日志記錄、告警通知全都不執(zhí)行了因?yàn)槟愕牧鞒谈緵]定義“失敗”分支。正確的做法是在主干流程的旁邊并行設(shè)計(jì)一條錯(cuò)誤處理支線主干上每個(gè)可能失敗的作業(yè)項(xiàng)都拉一條“失敗”連線到錯(cuò)誤統(tǒng)一處理作業(yè)項(xiàng)。這個(gè)錯(cuò)誤統(tǒng)一處理作業(yè)項(xiàng)里放什么我后面會(huì)詳細(xì)講這里先記住核心思想把錯(cuò)誤捕獲設(shè)計(jì)成作業(yè)圖里的一條平行支線而不是等作業(yè)失敗后再去看日志。2.3 作業(yè)與轉(zhuǎn)換之間的異常傳遞還有一個(gè)容易忽略的細(xì)節(jié)作業(yè)里的“轉(zhuǎn)換”作業(yè)項(xiàng)它執(zhí)行一個(gè)轉(zhuǎn)換時(shí)是等到轉(zhuǎn)換跑完才告知成功或失敗的。如果你的轉(zhuǎn)換里把錯(cuò)誤流都寫進(jìn)了一個(gè)表但轉(zhuǎn)換本身沒有報(bào)錯(cuò)作業(yè)就認(rèn)為它成功了——這其實(shí)是你故意想要的效果某些數(shù)據(jù)錯(cuò)誤可以被“處理”掉任務(wù)整體成功。但有一個(gè)問題轉(zhuǎn)換內(nèi)部有錯(cuò)誤流不代表轉(zhuǎn)換本身失敗作業(yè)并不知道發(fā)生過多少行錯(cuò)誤。更嚴(yán)謹(jǐn)?shù)淖龇ㄊ怯靡粋€(gè)“獲得行數(shù)”的輔助查詢或者向作業(yè)返回行數(shù)來判斷錯(cuò)誤是否超標(biāo)。Webspoon里有一種做法是在轉(zhuǎn)換的最后放一個(gè)“寫日志”步驟通過日志輸出錯(cuò)誤行數(shù)另一種更規(guī)范的做法是把錯(cuò)誤行數(shù)寫入一張控制表作業(yè)里緊接著用一個(gè)“表輸入”作業(yè)項(xiàng)去查這張表的錯(cuò)誤計(jì)數(shù)如果超過閾值就拋出異常。這種“錯(cuò)誤流控制表作業(yè)檢核”的組合就比單純掛一條失敗線要靈活得多因?yàn)槟阍谧鳂I(yè)層拿到了“這輪跑批錯(cuò)了多少行”這個(gè)關(guān)鍵指標(biāo)可以決定是繼續(xù)跑、重試還是直接終止。3. 實(shí)戰(zhàn)構(gòu)建一套可落地的全局異常捕獲體系3.1 搭建帶錯(cuò)誤流的轉(zhuǎn)換示例光說不練假把式我拿一個(gè)最常見的場(chǎng)景來走一遍完整操作從MySQL源表同步訂單數(shù)據(jù)到PostgreSQL目標(biāo)表中間做字段類型轉(zhuǎn)換和空值處理。先在Webspoon里新建一個(gè)轉(zhuǎn)換包含以下步驟鏈路表輸入讀取源訂單表SQL寫成SELECT * FROM source_orders WHERE update_time ${lastTime}使用時(shí)間參數(shù)支持增量抽取。這一步的錯(cuò)誤處理設(shè)置為錯(cuò)誤流輸出到“錯(cuò)誤日志寫入”步驟。字段選擇這一步做類型轉(zhuǎn)換比如把源端的amount字段字符串轉(zhuǎn)為數(shù)字類型。這里最容易報(bào)錯(cuò)——遇到非數(shù)字字符就拋異常。因此這一步驟必須開啟錯(cuò)誤處理錯(cuò)誤流同樣流向“錯(cuò)誤日志寫入”。空值處理設(shè)置某些非必填字段為空時(shí)填充默認(rèn)值比如city為空時(shí)填“未知城市”。表輸出寫入PostgreSQL目標(biāo)表主鍵沖突、字段超長等錯(cuò)誤都在這一步暴露。開啟錯(cuò)誤處理錯(cuò)誤流流向“錯(cuò)誤日志寫入”。錯(cuò)誤日志寫入這個(gè)步驟是“表輸出”類型把錯(cuò)誤流里的錯(cuò)誤代碼、錯(cuò)誤描述、錯(cuò)誤字段、出錯(cuò)的原始數(shù)據(jù)行一并寫入專門的etl_error_log表。在步驟2“字段選擇”里具體配置錯(cuò)誤處理時(shí)要注意啟用錯(cuò)誤處理之后錯(cuò)誤流會(huì)多出幾個(gè)內(nèi)置字段常見的是錯(cuò)誤描述、錯(cuò)誤代碼、錯(cuò)誤字段這些字段會(huì)帶上該行數(shù)據(jù)出錯(cuò)的上下文。你可以通過“選擇字段”步驟調(diào)整這些字段的順序和命名。錯(cuò)誤流的行數(shù)據(jù)保留了原始數(shù)據(jù)行的所有字段所以錯(cuò)誤日志表里既能看到這條數(shù)據(jù)本身也能看到為什么錯(cuò)。這里有個(gè)經(jīng)驗(yàn)錯(cuò)誤日志表的設(shè)計(jì)不要只存錯(cuò)誤信息一定要存出錯(cuò)的步驟名稱和數(shù)據(jù)快照。后續(xù)排查時(shí)你看到“字段選擇步驟字符串轉(zhuǎn)數(shù)字失敗值為abc位置在源表第1025行”效率要比對(duì)著完整日志猜高一個(gè)量級(jí)。3.2 作業(yè)調(diào)度與日志表配置轉(zhuǎn)換配好了還差一個(gè)殼子。在Webspoon里新建一個(gè)作業(yè)結(jié)構(gòu)這樣設(shè)計(jì)作業(yè)項(xiàng)A“開始”作業(yè)項(xiàng)B“執(zhí)行轉(zhuǎn)換”指向剛才搭好的那個(gè)轉(zhuǎn)換作業(yè)項(xiàng)C“SQL腳本”定義一個(gè)檢查步驟如果etl_error_log表里本次批次錯(cuò)誤數(shù)超過10拋出一個(gè)異常作業(yè)項(xiàng)D“發(fā)送郵件”發(fā)告警通知收件人設(shè)為ETL負(fù)責(zé)人作業(yè)項(xiàng)E“作業(yè)結(jié)束”關(guān)鍵在設(shè)計(jì)連線A成功 → BB成功 → CB失敗 → D這里直接在失敗分支上接告警任務(wù)起都起不來時(shí)也要發(fā)郵件通知C成功 → E錯(cuò)誤數(shù)在閾值內(nèi)作業(yè)正常結(jié)束C失敗 → D錯(cuò)誤數(shù)超閾值走告警D執(zhí)行完 → E這種設(shè)計(jì)下作業(yè)不會(huì)因?yàn)槟骋徊绞【汀奥惚肌币凑J瘴舶驯敬五e(cuò)誤行數(shù)控制在可控范圍內(nèi)要么帶著告警結(jié)束讓負(fù)責(zé)人第一時(shí)間知道。然后別忘了一個(gè)容易被忽略的配置——作業(yè)和轉(zhuǎn)換的日志記錄。Webspoon里作業(yè)可以配置日志表作業(yè)日志記錄轉(zhuǎn)換也可以配置日志表轉(zhuǎn)換日志記錄。配置好之后每次跑批的起止時(shí)間、執(zhí)行結(jié)果、日志文件路徑都會(huì)自動(dòng)記錄到數(shù)據(jù)庫表里。這里強(qiáng)烈建議單獨(dú)建一個(gè)etl_log庫不要在業(yè)務(wù)庫里混著放作業(yè)日志表JOB_LOG_ID、JOBNAME、STATUS、START_TIME、END_TIME、LOG_TEXT等通道日志表CHANNEL_LOG_ID、CHANNEL_ID、LOG_LEVEL、LOGGING_TEXT等有了這些表你就能寫SQL查詢哪些作業(yè)最近執(zhí)行失敗、每個(gè)作業(yè)平均耗時(shí)多少、哪一步報(bào)錯(cuò)次數(shù)最多。這已經(jīng)是Anthill級(jí)別的運(yùn)營視角了但對(duì)Kettle這種底層ETL工具來說很夠用。3.3 定時(shí)跑批與自動(dòng)告警聯(lián)動(dòng)Webspoon本身不帶一個(gè)特別成熟的調(diào)度中心雖然它可以通過carte啟動(dòng)遠(yuǎn)程執(zhí)行大多數(shù)生產(chǎn)環(huán)境里定時(shí)跑批是交給外部調(diào)度系統(tǒng)來觸發(fā)的常見的有在Linux服務(wù)器上配置crontab定時(shí)執(zhí)行pan.sh或kitchen.sh調(diào)用轉(zhuǎn)換和作業(yè)用Jenkins等CI/CD工具通過命令行或REST API觸發(fā)用SpringBoot集成Kettle在Java服務(wù)里嵌入調(diào)度邏輯這也是最近熱詞里SpringBoot集成Kettle的來源不管用哪種方式我的建議是不要把全局異常捕獲的能力全部押注在外部調(diào)度系統(tǒng)上。Kettle作業(yè)本身就要具備“報(bào)錯(cuò)-記錄-決定是否繼續(xù)”的能力否則你把任務(wù)交給外部調(diào)度器能看到的只是一個(gè)失敗信號(hào)細(xì)節(jié)還是丟的。你可以把外部調(diào)度器的角色定位為“定時(shí)拉起任務(wù)監(jiān)控告警”而Kettle內(nèi)部的作業(yè)和轉(zhuǎn)換負(fù)責(zé)“精細(xì)化錯(cuò)誤捕獲日志落庫”。兩者分工協(xié)作是最穩(wěn)的生產(chǎn)形態(tài)。舉個(gè)實(shí)例我在一個(gè)數(shù)據(jù)同步項(xiàng)目里就是在SpringBoot里寫了一個(gè)調(diào)度模塊每10分鐘向Webspoon的Carte服務(wù)發(fā)送HTTP請(qǐng)求執(zhí)行一個(gè)作業(yè)同時(shí)監(jiān)控作業(yè)日志表里的狀態(tài)字段一旦發(fā)現(xiàn)5分鐘內(nèi)連續(xù)三次失敗就往釘釘群機(jī)器人發(fā)一條告警。4. 常見問題與排查技巧實(shí)錄4.1 錯(cuò)誤流不輸出數(shù)據(jù)是怎么回事配置了錯(cuò)誤處理但錯(cuò)誤流一步都沒接住任何數(shù)據(jù)日志里也沒有報(bào)錯(cuò)這常常會(huì)讓人誤以為“沒出錯(cuò)”——但數(shù)據(jù)肉眼可見地少了。這個(gè)問題十有八九是出在步驟對(duì)錯(cuò)誤的吞沒上。舉例來說你用“字符串替換”步驟想把NULL替換成空值但源端的NULL到Kettle里其實(shí)是以null字符串存在的根本不會(huì)觸發(fā)錯(cuò)誤再比如你用“值映射”步驟映射一個(gè)不存在的枚舉值Kettle的默認(rèn)行為是返回原值而不是報(bào)錯(cuò)。這些步驟天然不具備“錯(cuò)誤拋出”的能力那也就不存在錯(cuò)誤流了。排查這類問題先確認(rèn)兩件事第一錯(cuò)誤是否真的是數(shù)據(jù)類型的轉(zhuǎn)換錯(cuò)誤而不是業(yè)務(wù)規(guī)則上的不匹配第二錯(cuò)誤處理的“目標(biāo)步驟”和“錯(cuò)誤字段名”是否配置齊全如果目標(biāo)步驟沒選上錯(cuò)誤數(shù)據(jù)流被丟棄錯(cuò)誤處理就形同虛設(shè)。4.2 作業(yè)日志表里看不到錯(cuò)誤詳情很多人配了作業(yè)日志表但錯(cuò)誤發(fā)生時(shí)日志表里只有一行“作業(yè)執(zhí)行失敗”具體哪個(gè)轉(zhuǎn)換的哪一步報(bào)錯(cuò)完全沒記下來。這種情況其實(shí)不是配置錯(cuò)了而是你混淆了“作業(yè)日志”和“轉(zhuǎn)換日志”的職責(zé)邊界。作業(yè)日志只記錄作業(yè)本身的生命周期不深入到轉(zhuǎn)換里的步驟級(jí)信息。要想把步驟級(jí)錯(cuò)誤記錄下來你必須在轉(zhuǎn)換里也配置轉(zhuǎn)換日志記錄最好再單獨(dú)開一個(gè)“寫日志”步驟把錯(cuò)誤流里的關(guān)鍵字段寫到文本日志文件。如果日志表里什么都查不到還有一個(gè)常見的低級(jí)坑日志表的連接策略配置不對(duì)。在“作業(yè)設(shè)置”的日志選項(xiàng)卡里需要單獨(dú)指定一個(gè)數(shù)據(jù)庫連接來存儲(chǔ)日志數(shù)據(jù)而不是使用業(yè)務(wù)數(shù)據(jù)源。我見過好幾次開發(fā)環(huán)境順手選了同一個(gè)業(yè)務(wù)庫連接結(jié)果生產(chǎn)環(huán)境的業(yè)務(wù)庫連接權(quán)限被回收日志寫不進(jìn)去作業(yè)直接就失敗了。4.3 Webspoon與桌面版Kettle的錯(cuò)誤處理差異Webspoon本質(zhì)上是Kettle的Web封裝核心引擎相同但在交互上有幾個(gè)讓人別扭的地方我在遷移過程中踩過不少坑。第一Webspoon里右鍵菜單的彈出速度受網(wǎng)絡(luò)影響有時(shí)候你點(diǎn)了步驟右鍵菜單半天出不來或者在步驟配置窗口切換標(biāo)簽頁時(shí)卡頓??焖俚奶娲桨甘前巡襟E配置頁的“錯(cuò)誤處理”標(biāo)簽頁固定為常用查看項(xiàng)先統(tǒng)一配置好再用快捷鍵切換。第二Webspoon默認(rèn)不保留客戶端的本地臨時(shí)文件錯(cuò)誤流如果要輸出到本地路徑路徑在服務(wù)器上必須存在而且權(quán)限要夠否則錯(cuò)誤倒是接住了寫文件又失敗了。第三Webspoon的日志界面刷新頻率低跑一個(gè)長轉(zhuǎn)換的時(shí)候你在頁面上看不到實(shí)時(shí)進(jìn)度這時(shí)候不要干等而是去數(shù)據(jù)庫查作業(yè)日志表和轉(zhuǎn)換步驟日志表用SQL看實(shí)時(shí)狀態(tài)。4.4 整理一份問題速查表我把自己在Webspoon生產(chǎn)環(huán)境里折騰異常捕獲時(shí)遇到的典型問題整理成一張速查表希望對(duì)大家有幫助癥狀可能原因排查方法轉(zhuǎn)換報(bào)錯(cuò)但錯(cuò)誤流沒數(shù)據(jù)不是行數(shù)據(jù)錯(cuò)誤而是資源連接類錯(cuò)誤檢查作業(yè)節(jié)點(diǎn)日志看堆棧異常錯(cuò)誤流有數(shù)據(jù)但日志表沒記錄錯(cuò)誤目標(biāo)步驟的類型或映射配錯(cuò)重新檢查“錯(cuò)誤日志寫入”步驟字段映射作業(yè)日志表查不到本次運(yùn)行記錄日志連接未配置或權(quán)限不足在作業(yè)設(shè)置里單獨(dú)指定日志連接測(cè)試連接錯(cuò)誤發(fā)生時(shí)作業(yè)立刻停止不走失敗支線連線約束條件寫成了“成功”編輯作業(yè)連線改為“失敗”或“不論成功與否”Webspoon頁面看不到詳細(xì)堆棧Web版日志展示弱日志文件寫在服務(wù)端查看服務(wù)器端logs目錄下的carte.log錯(cuò)誤數(shù)超過閾值沒有觸發(fā)告警“SQL腳本”作業(yè)項(xiàng)未正確返回失敗狀態(tài)在SQL腳本中顯式執(zhí)行SELECT 1/0來觸發(fā)異常這張表是我在實(shí)際支持別人問題時(shí)最常用到的角度建議截圖收藏或者貼在你的團(tuán)隊(duì)文檔里。5. 一些不太容易想到的實(shí)用技巧5.1 利用“復(fù)制行到結(jié)果”做錯(cuò)誤數(shù)據(jù)的二次分析有時(shí)錯(cuò)誤流接住的數(shù)據(jù)不直接寫日志表而是想臨時(shí)擱著等跑批結(jié)束后集中分析這批臟數(shù)據(jù)的長相。這時(shí)候可以用一個(gè)“復(fù)制行到結(jié)果”步驟把錯(cuò)誤流的數(shù)據(jù)復(fù)制到結(jié)果集里然后在作業(yè)后續(xù)的“表輸入”步驟通過變量或結(jié)果集引用再次讀取。這個(gè)方法特別好用因?yàn)槟憧梢园颜麄€(gè)錯(cuò)誤數(shù)據(jù)的全貌源字段、錯(cuò)誤原因、出現(xiàn)次數(shù)集中做一次數(shù)據(jù)剖析判斷是源頭數(shù)據(jù)格式變了還是E-R模型需要調(diào)整。我以前接一個(gè)“渠道訂單”項(xiàng)目時(shí)就是靠這個(gè)技巧把幾百條錯(cuò)誤數(shù)據(jù)拉出來統(tǒng)計(jì)發(fā)現(xiàn)其中60%都是同一類手機(jī)號(hào)格式錯(cuò)誤后來直接在源端加校驗(yàn)規(guī)則問題解決了一大半。5.2 用過濾步驟模擬自定義重試邏輯Kettle里沒有內(nèi)置“重試多少次”的機(jī)制但你可以用循環(huán)變量在作業(yè)層模擬。做法是在作業(yè)里放一個(gè)“循環(huán)”作業(yè)項(xiàng)利用變量計(jì)數(shù)器每次跑完轉(zhuǎn)換后通過“表輸入”作業(yè)項(xiàng)檢查錯(cuò)誤日志表里有幾條錯(cuò)誤如果錯(cuò)誤數(shù)大于2就重新執(zhí)行轉(zhuǎn)換最多循環(huán)3次。循環(huán)里記得設(shè)置一個(gè)休眠等待避免頻繁重試壓垮目標(biāo)數(shù)據(jù)庫。這套邏輯的實(shí)現(xiàn)關(guān)鍵是把“重試次數(shù)”也寫入日志表跑批記錄里一眼就能看到這次任務(wù)是第幾次重試跑成功的。這在審計(jì)和業(yè)務(wù)復(fù)盤的時(shí)候特別管用。5.3 優(yōu)先用變量管理錯(cuò)誤閾值錯(cuò)誤閾值別寫死在轉(zhuǎn)換里。在作業(yè)或轉(zhuǎn)換的“命名參數(shù)”里定義一個(gè)error_threshold默認(rèn)值設(shè)為5然后在錯(cuò)誤處理配置中引用這個(gè)參數(shù)。這樣做的好處是生產(chǎn)環(huán)境里想臨時(shí)調(diào)高容忍度比如大促期間源端數(shù)據(jù)確實(shí)亂成一鍋粥只需要在Webspoon的作業(yè)參數(shù)里改一個(gè)值不用改整個(gè)作業(yè)結(jié)構(gòu)。用參數(shù)管理閾值以后你還可以在“發(fā)送郵件”作業(yè)項(xiàng)里拼上當(dāng)前閾值、實(shí)際錯(cuò)誤數(shù)、本次跑批時(shí)間一封自動(dòng)生成的異常報(bào)告就出來了。這個(gè)郵件里的信息越詳細(xì)值班同學(xué)起床處理問題的反應(yīng)速度就越快。5.4 配合JSON解析場(chǎng)景的全局捕獲很多人在用Kettle從REST接口拉數(shù)據(jù)做增量同步熱詞里面也有“kettle調(diào)用get接口分頁抽取數(shù)據(jù)”和“kettle可以解析json嗎”。這類場(chǎng)景里全局異常捕獲的側(cè)重點(diǎn)又不太一樣。接口調(diào)用最常見的錯(cuò)誤是超時(shí)、HTTP狀態(tài)碼非200、返回JSON結(jié)構(gòu)不符合預(yù)期。我的建議是給這類轉(zhuǎn)換單獨(dú)設(shè)計(jì)一套“前置校驗(yàn)錯(cuò)誤流”在“HTTP”步驟后緊跟一個(gè)“驗(yàn)證JSON”步驟或者用“JavaScript代碼”步驟做JSON Schema校驗(yàn)這一步的錯(cuò)誤流把非法的JSON原文和URL記錄下來。同時(shí)把HTTP狀態(tài)碼字段納入錯(cuò)誤日志表排查時(shí)一眼就能看出是400、500還是超時(shí)導(dǎo)致。這些處理看起來增加了一點(diǎn)步驟數(shù)量但排障效率提升非常明顯。特別是分頁抽取時(shí)某一頁接口掛了它能精確告訴你是第幾頁出了事而不會(huì)讓你從頭到尾跑一遍才能復(fù)現(xiàn)問題。6. 多環(huán)境部署下的全局異常設(shè)計(jì)6.1 開發(fā)、測(cè)試、生產(chǎn)環(huán)境的日志通道隔離Webspoon同一個(gè)環(huán)境里可以配置多個(gè)資源庫不同環(huán)境的etl_error_log表絕對(duì)不能混用。開發(fā)環(huán)境里你可能會(huì)故意制造很多臟數(shù)據(jù)來測(cè)試錯(cuò)誤流公司和生產(chǎn)環(huán)境的數(shù)據(jù)要是串了那才叫災(zāi)難。建議的做法是不同環(huán)境配置不同的數(shù)據(jù)庫連接并且在etl_error_log表里加一個(gè)env字段寫死開發(fā)/測(cè)試/生產(chǎn)標(biāo)識(shí)。這樣即使不小心跑錯(cuò)了庫也能在數(shù)據(jù)上快速定位。6.2 調(diào)度中心與Webspoon的配合如果你所在的團(tuán)隊(duì)已經(jīng)有統(tǒng)一的調(diào)度平臺(tái)比如阿里的DataWorks風(fēng)格或者自研的任務(wù)調(diào)度系統(tǒng)那么Webspoon的身份就是“執(zhí)行引擎中的一個(gè)worker”。在這種架構(gòu)下全局捕獲的關(guān)鍵是讓調(diào)度平臺(tái)拿到明確的“健康信息”作業(yè)跑沒跑完、錯(cuò)誤行數(shù)是多少、耗時(shí)多少。你可以通過Webspoon提供的REST API或者數(shù)據(jù)庫日志表來暴露這些指標(biāo)調(diào)度平臺(tái)只要定期查詢這些指標(biāo)就能完成對(duì)ETL任務(wù)的全生命周期調(diào)度。我見過一個(gè)把Webspoon接入自研調(diào)度平臺(tái)的項(xiàng)目轉(zhuǎn)型路上最大的阻力不是技術(shù)而是錯(cuò)誤語義不統(tǒng)一Kettle認(rèn)為的“成功”和調(diào)度平臺(tái)認(rèn)為的“成功”定義不同。后來我們干脆在Kettle作業(yè)的最外層包了一個(gè)“總控作業(yè)”最后一步執(zhí)行一個(gè)“寫日志”步驟把成功/失敗狀態(tài)、錯(cuò)誤計(jì)數(shù)、批次號(hào)統(tǒng)一寫成一條記錄。調(diào)度平臺(tái)只認(rèn)這張表里最新一條記錄的STATUS字段問題徹底解決。7. 最后分享一點(diǎn)我自己的體會(huì)做了這么多年ETL被各種跑批事故折騰過無數(shù)次之后我最大的感悟是捕獲異常不是為了讓任務(wù)不失敗而是為了讓失敗變得可預(yù)期、可回溯、可處理。全局異常/錯(cuò)誤捕獲這套體系搭好之前你每天最怕的就是那封紅色的告警郵件搭好之后你會(huì)覺得紅色郵件反而是最可愛的——因?yàn)樗涯銖臒o限猜測(cè)中解放出來直接告訴你問題在哪。Webspoon對(duì)于小團(tuán)隊(duì)、輕量級(jí)的數(shù)據(jù)同步需求來說仍然是一個(gè)性價(jià)比極高的選擇不要因?yàn)樗缑娌粔颥F(xiàn)代就看輕它。在默認(rèn)原生Kettle和重型商業(yè)化ETL平臺(tái)之間它處在一個(gè)非常舒服的中間位置功能完整、部署輕量、還能通過外部調(diào)度器二次包裝成標(biāo)準(zhǔn)的數(shù)據(jù)平臺(tái)組件。希望這篇關(guān)于全局異常/錯(cuò)誤捕獲的課程筆記能幫你少踩幾個(gè)我踩過的坑。如果你在Webspoon里配置錯(cuò)誤處理時(shí)遇到過什么經(jīng)典的奇葩問題歡迎帶著場(chǎng)景來交流ETL這個(gè)領(lǐng)域的坑是永遠(yuǎn)挖不完的每次能把坑填平一點(diǎn)點(diǎn)就是在為同行鋪路了。