:從桌面工具到輕量級ETL調(diào)度平臺)
簡介KettlePentaho Data Integration的Web版部署包面向需要把ETL能力遷移到瀏覽器端的數(shù)據(jù)工程師、運維人員與數(shù)據(jù)平臺建設(shè)者用于快速搭建webKettle在線數(shù)據(jù)集成環(huán)境適合分布式團隊、遠(yuǎn)程訪問和可視化拖拽設(shè)計等場景。資源共包含1002個文件壓縮包約157.7MB其中jar與class文件提供核心依賴和編譯邏輯png、svg、gif等構(gòu)成界面圖標(biāo)與步驟節(jié)點素材xml、properties、jsp負(fù)責(zé)服務(wù)配置和頁面交互bat、sh腳本便于在Windows與Linux下啟停服務(wù)。整體目錄結(jié)構(gòu)清晰覆蓋部署webKettle所需的程序、配置和前端資源可部署至Tomcat后直接使用。已有1545人學(xué)習(xí)下載。通過這份資源讀者能省去逐一下載組件的麻煩快速獲得一個可運行的Web化ETL環(huán)境在瀏覽器中使用Spoon的圖形化能力完成轉(zhuǎn)換、作業(yè)調(diào)度與多數(shù)據(jù)源接入也可作為后續(xù)擴展權(quán)限體系、對接REST API、構(gòu)建數(shù)據(jù)治理平臺的基線。前陣子整理公司內(nèi)部工具時翻出一個有點年頭的壓縮包叫kettle的web版.zip。這是我當(dāng)時給數(shù)據(jù)部門做的調(diào)度平臺內(nèi)核——外面一層Web服務(wù)里面真正干活的是Kettle引擎。時隔這么久再看這個包還挺有代表意義的所以決定把這套東西的來龍去脈、實現(xiàn)思路和踩過的坑完整寫出來。這篇文章適合兩類人看一類是天天用Kettle做ETL被“跑完還得守著看結(jié)果”這件事折磨的工程師另一類是想把Kettle能力封裝成平臺給團隊或客戶提供在線化數(shù)據(jù)加工服務(wù)的開發(fā)者。我會從需求分析、技術(shù)選型、核心代碼、打包部署一路講到問題排查全程按實際項目的完整流程走一遍所有關(guān)鍵環(huán)節(jié)都可以直接參考落地。1. 為啥要把Kettle做成Web版1.1 桌面版的幾個硬傷Kettle全稱Pentaho Data Integration做數(shù)據(jù)抽取、轉(zhuǎn)換、加載確實很強。但原生的Spoon是純桌面應(yīng)用用久了你會發(fā)現(xiàn)幾個特別難受的點。一是任務(wù)調(diào)度基本靠手動。定時調(diào)度要么靠系統(tǒng) crontab 強配要么借助第三方工具輔助觸發(fā)管理起來很別扭。二是團隊協(xié)作完全沒體系。一個復(fù)雜轉(zhuǎn)換需要多人改改完拷貝、傳輸、覆蓋時間一長版本就亂了誰改過什么根本說不清。三是監(jiān)控能力約等于零。作業(yè)跑起來后想知道執(zhí)行到哪一步、報了什么錯、數(shù)據(jù)量多少都得靠人肉盯界面和日志。四是沒法做權(quán)限控制。懂技術(shù)的人拿到腳本就能改部門內(nèi)部還好一旦面對客戶和跨團隊場景就完全失控。這些硬傷背后其實指向同一個需求——把Kettle從“開發(fā)者的桌面上”搬到“服務(wù)器上”再通過Web界面把能力暴露出去。換句話說Kettle負(fù)責(zé)底層數(shù)據(jù)處理Web平臺負(fù)責(zé)調(diào)度、監(jiān)控、權(quán)限和資源管理。1.2 Web版到底要解決什么問題我當(dāng)時做Web化的時候給自己定的核心目標(biāo)就四個支持在線部署ktr和kjb文件、支持配置定時調(diào)度、支持實時查看執(zhí)行日志、支持按用戶分配執(zhí)行權(quán)限。第一個目標(biāo)是地基得能讓人把本地開發(fā)好的ETL流程傳上來在服務(wù)器上跑通。第二個目標(biāo)對應(yīng)的是真實場景里最頻繁的動作——每天凌晨跑一次全量同步每小時增量拉取月底匯總報表都屬于周期性調(diào)度。第三個目標(biāo)是剛需執(zhí)行日志看不到出了問題根本沒法排查。第四個目標(biāo)是走向平臺化的前提不能讓所有人都能改別人的轉(zhuǎn)換。把這四個目標(biāo)想清楚后再看“Web版”這仨字就明白它本質(zhì)上不是一個“用瀏覽器打開Spoon”的Demo而是一個有實戰(zhàn)價值的輕量級調(diào)度平臺。2. 四條技術(shù)路線我最終選了哪條2.1 先看各家方案的長短板Kettle做Web化其實不止一條路。我把實際項目里出現(xiàn)過的方案整理成了對比表方便你根據(jù)自己情況判斷。技術(shù)路線實現(xiàn)方式優(yōu)點缺點適用場景Carte 前端殼直接部署Kettle自帶的Carte服務(wù)前端頁面遠(yuǎn)程調(diào)用開發(fā)量小、Kettle原生支持調(diào)度太弱、監(jiān)控簡單、無法做復(fù)雜權(quán)限臨時給用戶提供頁面入口引擎二次開發(fā)在Java應(yīng)用中直接引入Kettle引擎API自己封裝服務(wù)和接口可完全按需求定制、能力強、擴展性好開發(fā)量較大、對Kettle原理要求高做平臺化產(chǎn)品Pentaho Server直接用官方商業(yè)平臺套件功能全、自帶權(quán)限和調(diào)度重、貴、定制難、對國內(nèi)環(huán)境不友好預(yù)算充足的大企業(yè)開源項目改造在現(xiàn)成開源項目如HiKari、kettle-manager基礎(chǔ)上改造省時省力項目停更風(fēng)險、二次開發(fā)受限于原作者設(shè)計要求不高、快速起步很多朋友總想撿現(xiàn)成的但現(xiàn)實是現(xiàn)成方案要么重要么糙真正想讓Kettle在業(yè)務(wù)里發(fā)揮價值老老實實走第二條路線——引擎二次開發(fā)才是能長期演進(jìn)的路子。2.2 選引擎二次開發(fā)的原因我最終選了引擎二次開發(fā)這條路核心原因有三個。第一Kettle本身就是一個Java庫。它叫“工具”也叫“平臺”本質(zhì)上是一堆可復(fù)用的引擎API。KettleEnvironment.init()、TransMeta、JobMeta這些類可以直接在Java工程里用這就意味著我可以完全掌控執(zhí)行流程——執(zhí)行前注入變量、執(zhí)行中監(jiān)聽狀態(tài)、執(zhí)行后收集日志每一個環(huán)節(jié)都可以自定義。第二業(yè)務(wù)需求是明確的方案必須匹配需求。我需要的不是一套大而全的平臺而是一個能快速接入Spring Boot項目、能靈活控制執(zhí)行邏輯的內(nèi)核。Carte的調(diào)度能力太雞肋Pentaho Server又過于笨重只有引擎二次開發(fā)能在“可控”和“靈活”之間找到平衡點。第三可持續(xù)維護(hù)性。自己做引擎封裝后續(xù)加功能、修Bug、調(diào)性能都有底不用等上游社區(qū)更新。項目交付出去自己心里有數(shù)。3. 核心實現(xiàn)引擎調(diào)用、參數(shù)變量和作業(yè)調(diào)度3.1 工程依賴和最小可運行骨架先說工程搭建。Web服務(wù)本身用的Spring Boot構(gòu)建工具M(jìn)aven。Kettle引擎的依賴版本非常關(guān)鍵一定要用與本地Spoon一致的版本防止ktr文件不兼容。dependency groupIdorg.pentaho.di/groupId artifactIdkettle-core/artifactId version8.3.0.0-428/version /dependency dependency groupIdorg.pentaho.di/groupId artifactIdkettle-engine/artifactId version8.3.0.0-428/version /dependency注意Kettle對JDK版本很敏感8.x系列建議用JDK 89.x以后才逐步兼容高版本JDK。如果你本地Spoon是10.x那依賴也要對應(yīng)升級到10.x接口在細(xì)節(jié)上有不小差異。一個最小可運行的骨架大概是下面這樣的啟動時初始化環(huán)境然后由接口接收ktr文件路徑交給執(zhí)行服務(wù)去跑執(zhí)行過程中通過回調(diào)收集日志。SpringBootApplication public class KettleWebApplication { public static void main(String[] args) { SpringApplication.run(KettleWebApplication.class, args); // 初始化Kettle引擎環(huán)境 KettleEnvironment.init(); } }這個初始化動作對應(yīng)著.kettle目錄下各種配置文件的加載如果初始化失敗后面所有操作都會卡住所以前面環(huán)境配置一定要做對。3.2 加載并執(zhí)行轉(zhuǎn)換的完整代碼加載轉(zhuǎn)換并執(zhí)行是整個Web版最核心的一段代碼也是我當(dāng)時最先跑通的模塊。直接看這段代碼它是整個Web平臺的基礎(chǔ)。public MapString, Object executeTransformation(String ktrPath, MapString, String params) { MapString, Object result new HashMap(); try { // 1. 加載轉(zhuǎn)換元數(shù)據(jù) TransMeta transMeta new TransMeta(ktrPath); // 2. 創(chuàng)建轉(zhuǎn)換實例并注入變量 Trans trans new Trans(transMeta); if (params ! null) { params.forEach(trans::setVariable); } // 3. 添加日志監(jiān)聽 KettleLogLayout logLayout new KettleLogLayout(true); KettleLoggingEvent event new KettleLoggingEvent(null, new Object[] { logLayout }, LogLevel.ROWLEVEL, new Date()); // 實際項目中這里要把log事件轉(zhuǎn)發(fā)到WebSocket等通道 // 4. 執(zhí)行轉(zhuǎn)換等待完成 trans.execute(null); trans.waitUntilFinished(); // 5. 獲取執(zhí)行結(jié)果 result.put(success, trans.getErrors() 0); result.put(errors, trans.getErrors()); result.put(processedRows, trans.getTotalStepNr()); } catch (Exception e) { result.put(success, false); result.put(message, e.getMessage()); } return result; }第2步是最容易踩坑的地方。很多人在本地用Spoon跑轉(zhuǎn)換時喜歡直接在步驟里寫死路徑和數(shù)據(jù)源一到Web端執(zhí)行就報錯——因為沒有配置文件、沒有資源庫。解決方案就是靠Kettle的變量機制把文件路徑、數(shù)據(jù)庫連接、目標(biāo)表名這些全抽成變量由Web平臺在啟動時注入。代碼第4步的exec方法在后臺線程跑waitUntilFinished會阻塞等待。如果是Web接口建議把執(zhí)行邏輯放到線程池里避免HTTP請求長時間占用。如果需要監(jiān)控給Kettle加一個日志監(jiān)聽器將執(zhí)行日志實時推送到前端頁面展示。3.3 參數(shù)變量的三種傳法參數(shù)變量是Kettle Web化過程中最基礎(chǔ)也最重要的能力。當(dāng)年論壇熱搜詞里天天有人問“給出一套Kettle中參數(shù)變量的案例”真實場景中確實太常用了。Kettle里有三種傳參方式我在這里直接整理出來。傳遞方式變量生命周期使用場景示例全局屬性整個JVM進(jìn)程內(nèi)數(shù)據(jù)庫連接、固定路徑setVariable(db_host, 10.1.1.100)轉(zhuǎn)換變量當(dāng)前轉(zhuǎn)換生命周期時間窗口、批次號${etl_date}命令行參數(shù)單次執(zhí)行動態(tài)指定輸入文件等trans.execute(new String[]{arg1})最關(guān)鍵的是ktr文件內(nèi)部引用變量的語法是${varName}比如數(shù)據(jù)庫連接URL寫成jdbc:mysql://${db_host}:${db_port}/${db_name}在Web端執(zhí)行前統(tǒng)一注入就能一套轉(zhuǎn)換多環(huán)境通用。判斷變量有沒有傳對我教大家一個土辦法在轉(zhuǎn)換里加一個“寫日志”步驟把關(guān)鍵變量直接輸出到日志面板。只要啟動日志里能看到正確的變量值后邊所有依賴這個變量的配置就都走通了。4. 資源庫、zip打包與分發(fā)的完整姿勢4.1 資源庫選型文件型與數(shù)據(jù)庫型資源庫就是Kettle存儲轉(zhuǎn)換和作業(yè)元數(shù)據(jù)的地方。做Web版的時候資源庫的選型直接影響整體架構(gòu)設(shè)計。常見的資源庫有兩種形態(tài)文件型資源庫就是一個目錄里面是一堆XML文件好處是輕便直接拷走就能用壞處是不支持并發(fā)寫操作多人同時改動容易出問題數(shù)據(jù)庫型資源庫是把轉(zhuǎn)換和作業(yè)存進(jìn)數(shù)據(jù)表里Kettle官方把它們叫R_STEP_TYPE、R_TRANSFORMATION這些表好處是支持多用戶并發(fā)、天然適合Web平臺壞處是初始化表和配套索引等工作要自己確認(rèn)。我在項目里建議的是開發(fā)階段用文件型部署上生產(chǎn)后切數(shù)據(jù)庫型。尤其很多企業(yè)用達(dá)夢數(shù)據(jù)庫做資源庫需要注意達(dá)夢驅(qū)動對Kettle的兼容性連接參數(shù)要按達(dá)夢官方文檔調(diào)整。4.2 zip包目錄結(jié)構(gòu)和啟動腳本項目最終交付的形式就是一個zip壓縮包這也是標(biāo)題里“zip”二字的由來。生產(chǎn)環(huán)境不能要求運維懂Java、懂Maven一個解壓就能跑的包才是合格交付物。我的zip包目錄結(jié)構(gòu)是這么設(shè)計的kettle-web/ ├── bin/ │ ├── startup.bat │ └── startup.sh ├── lib/ │ └── (全部依賴jar包) ├── conf/ │ └── application.yml ├── templates/ │ └── kettle/ │ ├── transformations/ │ └── jobs/ ├── logs/ └── README.txtbin/startup.sh腳本里要固定JVM參數(shù)、編碼參數(shù)和主類路徑下面是我當(dāng)時用的模板。#!/bin/bash JAVA_OPTS-Xms512m -Xmx2048m -Dfile.encodingUTF-8 nohup java $JAVA_OPTS -jar ../lib/kettle-web.jar \ --spring.config.location../conf/application.yml \ ../logs/web.log 21 這里有兩個細(xì)節(jié)很多人不知道。第一-Dfile.encodingUTF-8必須加否則在Linux服務(wù)器上跑ktr時中文注釋和中文數(shù)據(jù)會亂碼。第二-Xmx不能太大Kettle跑大數(shù)據(jù)量轉(zhuǎn)換時JVM堆內(nèi)存和PermGen/Metaspace都需要預(yù)留但給得太大反而容易在容器環(huán)境里觸發(fā)操作系統(tǒng)OOM Killer。打包的時候也有講究。如果用Windows自帶右鍵壓縮有可能把隱藏文件和鎖定文件一起打進(jìn)去。我一般推薦命令行工具在項目根目錄執(zhí)行zip -r kettle-web.zip . -x *.git* -x *.idea* -x *.DS_Store這樣打出來的包干凈、體積小、目錄結(jié)構(gòu)完整。4.3 打包與交付的注意事項關(guān)于zip交付有幾個很容易翻車的地方必須單獨拎出來說。一是目錄層級的問題。壓縮時如果把最外層目錄也壓進(jìn)去了運維解壓后會得到一個kettle-web/kettle-web/...的雙層目錄影響體驗。正確做法是在kettle-web的上一級目錄執(zhí)行壓縮命令確保解壓后第一層就是bin、lib這些目錄。二是lib目錄完整性問題。Java項目打jar包時mvn package生成的只有一個可執(zhí)行jar但Kettle引擎本身依賴了很多擴展jar如果在服務(wù)器上用外置lib目錄部署就一定把所有依賴都拷貝到lib下。建議用mvn dependency:copy-dependencies把依賴全部導(dǎo)出。三是README別糊弄。運維不看代碼他們只關(guān)心三個問題JDK版本是多少、端口是哪個、配置文件里哪幾個參數(shù)必須改。我在README里把這三件事用加粗字體寫在最前面交付后問詢量瞬間少八成。5. 常見問題與排查技巧實錄5.1 invalid zip archive: could not find eocd這是Kettle在導(dǎo)入資源包或者加載插件時特別常見的一個報錯。invalid zip archive: could not find eocd的意思是“找不到zip壓縮包中央目錄結(jié)尾標(biāo)志”說白了就是這個zip文件是壞的或者根本不完整。實際項目里遇到這個報錯先別急著懷疑Kettle。按照這個順序排查基本不出錯。# 1. 檢查文件完整性 unzip -t your_file.zip # 2. 查看文件大小是否和源文件一致 ls -l your_file.zip # 3. 用無緩存的方式重新下載我遇到過的真實情況有三種一是從Windows上傳zip到Linux服務(wù)器時傳輸中斷導(dǎo)致文件不完整二是文件本身是加密壓縮包直接當(dāng)普通zip去讀三是文件被錯誤的工具改過擴展名比如把一個.rar直接改名為.zip。排查時注意別在第一步就走錯方向。5.2 中文亂碼與內(nèi)存不足中文亂碼是Kettle Web化之后出現(xiàn)頻率最高的“水土不服”問題。本地Windows跑得好好的部署到Linux服務(wù)器上就亂碼原因是Spoon在圖形界面里默認(rèn)了和服務(wù)器不一致的字符集。解決思路分兩層。第一層是JVM層面啟動參數(shù)加-Dfile.encodingUTF-8同時把Kettle配置文件里的KETTLE_DEFAULT_ENCODING設(shè)為UTF-8。第二層是數(shù)據(jù)源層面如果數(shù)據(jù)庫連接是GBK編碼ktr里的“表輸入”步驟就要顯式設(shè)置字符集。內(nèi)存不足的報錯通常是java.lang.OutOfMemoryError: Java heap space。這里有個容易搞混的點Kettle引擎執(zhí)行時有兩塊內(nèi)存要關(guān)注JVM堆內(nèi)存是處理數(shù)據(jù)行時用的Metaspace是加載類定義時用的。大數(shù)據(jù)量轉(zhuǎn)換建議-Xmx給到物理內(nèi)存的1/4左右Metaspace單獨設(shè)個256m。5.3 failed to copy spatial iop zip 這類“解壓復(fù)制失敗”的通用排查思路網(wǎng)上搜索Kettle相關(guān)內(nèi)容時經(jīng)常能看到failed to copy spatial iop zip的報錯混進(jìn)來。雖然這個報錯原文出自別的安裝包場景但它的排查思路對處理Kettle相關(guān)zip問題同樣適用。這類“復(fù)制或解壓文件失敗”的報錯四個原因最典型第一個是磁盤空間不足zip解壓需要的是臨時空間和最終空間之和建議先df -h查看剩余空間第二個是文件被占用Windows下殺毒軟件實時監(jiān)控會鎖定正在解壓的文件導(dǎo)致復(fù)制失敗第三個是壓縮包內(nèi)文件路徑過長Windows舊版本解壓時260字符路徑限制很容易觸發(fā)第四個是文件損壞用unzip -t測試即可。記住一個通用的處理思路遇到zip相關(guān)問題先測試完整性再檢查磁盤和權(quán)限最后考慮殺軟和路徑長度。這個順序能覆蓋90%的問題。寫在最后Kettle Web化這條路本質(zhì)上是一次“把單機工具改造成平臺能力”的工程實踐。我在做完這個項目后最大的體會是工具的價值往往不在工具本身而在于誰能把它嵌入到更高效率的流程里。Kettle執(zhí)行引擎的特性決定了它非常適合作為數(shù)據(jù)處理的中臺內(nèi)核來使用。最后再分享一個小技巧。如果你也想自己動手做Web版不要一開始就追求把Kettle全部功能搬上去。先跑通“上傳ktr、配置定時、查看日志”這個最小閉環(huán)再逐步增加權(quán)限管理、數(shù)據(jù)源管理、告警通知這些增值功能。一個能穩(wěn)定跑核心流程的版本遠(yuǎn)比一個功能列表很長但處處有Bug的版本有價值。本文還有配套的精品資源點擊獲取