Azkaban調研
在作業設計器中,實用哪種作業工作流的引擎是最主要的,現在對Azkaban和Oozie進行調研。
一.Azkaban介紹
Azkaban是一個類似於Oozie的任務調度系統,它以flow爲執行單位進行調度,flow爲預定義好的一個或者多個有依賴關係的Job工作流。同時它兼容所有的Hadoop版本,使用Web界面追蹤每個任務的執行情況並且提供了郵件的支持。
Azkaban主要有三個組件組成:
名稱 |
作用 |
Mysql |
項目以及執行計劃(所有任務的屬性信息、執行計劃、執行的結果以及輸出),每次執行情況等信息 |
Web服務器 |
使用jetty做爲web容器,提供服務 |
Executor執行服務器 |
負責具體的工作流的提交和執行,可以提交啓動多個執行服務器,他們通過mysql進行協調任務執行。 |
下面將通過Azkaban提供的solo(類似於單例模式)模式,對其提交和執行工作流做測試。
二.執行工作流流程
入下圖所示,爲Azkaban的操作主頁面。
經過調研,Azkaban可以調度的任務類型有:shell命令,Java程序,MR,Spark,Hive。下面將通過對以上幾類任務進行調度過程調研
2.1 shell語言工作流
創建工程:
一個工程包含一個或者多個flows,一個flows包含多個job。job是在Azkaban中運行的一個進程,一個job可能依賴於別的job,多個job和他們之間的依賴組成圖表稱爲flow。
Job創建:
只要創建一個以.job爲結尾的文本文件就可以,例如:
這種type爲command,告訴Azkaban使用linux的命令或者shell腳本去執行。這是一個job,如果是多個job我們可以定義flow。
Flow創建:
定義多個job以及job之間的依賴就可以組成flow。定義依賴可以使用dependencies參數就可以了。例如創建了4個job:
start.job |
type=command command=sleep 5 command.1=echo "start execute" |
test.job |
type=command command=sleep 3 command.1=echo "Hello World" |
sleep.job |
type=command dependencies=test, start command=sleep 10 |
finish.job |
type=command dependencies=sleep command=sleep 10 |
在以上4個job中,第三個sleep.job是依賴於test和start.job。也就是sleep的job執行順在需要等待test和start兩個job執行完成。同理finish依賴於sleep,finishi.job在sleep執行成功後再進行。
將這4個.job文件打包成zip文件上傳至Azkaban服務器(目前Azkaban只支持zip格式的文件上傳)。之後會生成job之間的關係以及一張DAG flow圖。
當點擊執行任務後,會依次執行節點並且記錄節點的運行情況。
其中會記錄每個job的運行情況以及job的輸出。
2.2 MR,Spark任務執行
同理Shell任務的job執行次序,底層修改只是調用不同的shell語句進行執行。例如使用spark做爲測試。
首先寫好一個job文件:
type=command
command=spark-submit --master spark://10.2.216.33:7077 --class com.test.AzkabanTest testSparkAzkaban.jar
將文件與測試的testSparkAzkaban.jar進行打包zip格式。上傳至服務器。運行成功後jobLogs會打印如下結果:
同理MR Job將jar包以及輸出的文件也可以放在HDFS中,通過shell語言進行。
三.執行的任務類型
Azkaban的使用方式是按照job文件描述來進行使用的,使用type來執行執行任務的類型,通過dependencies可以用來連接任務,即dependencies=A,只有A任務執行完了纔可以執行改任務。
其執行任務的類型有:
1.Shell語言類型,在上面已經有相應的解釋。
2.Java文件:使用Java和HadoopJava兩種類型。這兩種是插件形式操作java作業,統一要求的是操作從run方法進行執行,而不是main方法。
3.Hive.
Type=hive
User.to.proxy=azkaban
Azk.hive.action=execute.query
Hive.query.01=sql
四.Azkaban的執行缺陷
由於Azkaban中的每個job都是一個進程,在Azkaban中判斷job成功與否是根據這個進程是否成功執行完成,但是在MR 或者Spark Job執行的過程中,如果代碼出錯,運行在集羣上的任務會停止,並不會有內容寫入目標文件中,此時返回給Azkaban的進程是執行成功的,也就是job節點執行成功。這與任務執行的結果相悖。
例如:
在執行某個jar包的過程中時,出現了NullPointException,此時MR作業停止,但是最終Process 顯示的爲執行成功。並且節點最終執行的結果也爲成功:
所以爲了防止依賴的節點出現錯誤,其以下節點仍可運行的情況。需要換一個校驗job是否正確執行的維度進行評判,比如檢測MR 或者 Spark 任務的log文件是否正確執行等,或者檢測集羣中的任務是否執行成功。
總結:在執行結束後可以返回hdfs中查詢是否有對應的文件生成,如果有則表示成功,沒有則表示失敗。
五.Azkaban的源碼閱讀
首先從客戶端發送請求入手,客戶端會將所有任務封裝job防止在zip中,其主要邏輯爲:
1.調用LoginAbstractAzkabanServlet。如果是執行任務,會調用其子類ExecutorServlet中的handleAjaxAction。方法。其中會生成一個ExecutableFlow,這個對象中包含了整個工作流的基本信息。
2.生成了exeFlow之後調用executorManager用於提交任務。在這個任務中,會對exeFlow進行資源加鎖,填充exeFlow的屬性後會上傳該工作流的信息到數據庫中。最後調用dispatch 調度任務。
3.最終經過重重調用會生成一個ExecutorApiClient作爲RestClient向Azkaban發送任務請求。PS.發送的請求是GET請求。傳輸了一個任務的id。所以以上的所有對exeFlow的操作都是存儲與本地的操作。
在服務器端,主要的AzkabanServer包含以下屬性:
使用Socket連接的Server以及一個隊列線程池。QueuedThreadPool。
1.剛剛在客戶端發送的請求是一個Get請求,所以操作是使用doGet方法。該方法中調用handleAjaxExecute。
2.此方法中調用FlowRunnerManager.submitFlow(flowId),在服務器端根據flowId從數據庫中獲取了指定的flow。
3.根據flow以及相關配置信息會生成一個FlowRunner線程,將此線程放置在線程池中進行執行。其中會使用遞歸找出所有Ready的節點進行執行。在該線程中會在run()方法中不斷執行用戶需要的command。
在該方法中會封裝command成一個AzkabanProcess繼承了process類,調用start()方法去執行相關的命令。
PS 所以在Azkaban中無論執行hadoop還是spark 任務都可以通過設置命令來執行。但是這些命令操作是在Azkaban的服務器上進行的,由於每次操作一條command會生成一個進程去處理,進程量大很可能會影響到服務器的工作。
同時如果直接將command交給執行的用戶去執行,可能會出現安全問題。而且對於非技術同學操作困難很大。所以未來如果使用Azkaban進行開發時,需要分析用戶的操作生成command,不要將編輯command的入口交給用戶。