第四章節、配置以及運行一個任務(下) – spring batch


(申明:初嘗翻譯,未經校驗,請勿轉載)

4.3、配置一個“任務啟動器”

最簡單的“任務啟動器(JobLauncher)”接口的實現是“SimpleJobLauncher”。它只依賴於一個“任務存儲器(JobRepository)”,使得擁有任務執行的能力:

4.3-1

只要擁有了“任務執行器(JobExecution)”,它將調用“任務”的執行方法,最終將“任務執行器”返回給調用者:

image

當從一個調度器開始啟動時,執行順序是徑直的,並且運作良好。但如果從一個網站請求開始啟動,那么問題就來了。在這個場景中,運行的過程需要是異步的,才能使得“SimpleJobLauncher”立即返回一個結果給調用者。這是因為長時間掛起一個網站請求而去運行一個批量任務肯定是行不通的。如下圖時序圖所示:

image

通過配置一個“子任務執行器(TaskExecutor)”這樣簡單的操作,就能使得“SimpleJobLauncher”適用於這個場景:

image

任何一個“子任務執行器”接口的實現、都能被用作去控制任務的異步執行。

4.4、運行一個任務

在運行一個任務時,有兩樣東西是必不可少的:需要運行的“任務(Job)”和一個“任務啟動器(JobLauncher)”。他倆既可以放在同一個上下文中,也可以放在不同的上下文里。比如,當從命令啟動一個任務時,一個新的Java虛擬機(JVM)將被實例化用來服務每個任務,如此一來,每個任務便有了它自己的“任務啟動器(JobLaucher)”。但如果通過Web容器,通過“頁面請求(HttpRequest)”,通常只會存在一個“任務啟動器(JobLauncher)”,並且被配置為異步啟動模式,這樣多個請求才能並行不悖。

4.4.1、從命令行啟動任務

對於想要從一個企業級別調度器運行任務的用戶來說,命令行是首選的。這是因為大多數調度器(Quartz是個例外,使用了NativeJob)直接以操作系統進程的形式運行,普遍的通過系統腳本開始啟動。除此之外還有很多方式啟動一個java進程,比如Perl,Ruby,甚至諸如ant或maven這樣的“構建工具(build tools)”。但因為大多數人習慣於腳本,這個例子將主要關注這個。

4.4.1.1、命令行任務啟動器(CommandLineJobRunner)

因為腳本啟動一個任務必須通過java虛擬機,所以需要一個包含有“main”方法的類用來作為程序的入口。Spring Batch提供了一個實現恰能滿足這個要求:“命令行任務啟動器(CommandLineJobRunner)”。必須注意到這只是啟動任務的一種方法,除此之外有大量其它的方式啟動一個java進程,千萬不要以為這是必由之路。“命令行任務啟動器(CommandLineJobLauncher)”運行為四大步驟:

加載適當的應用上下文
解釋命令行參數為“任務參數(JobParameter)
基於參數定位適當的任務
使用“任務啟動器(JobLauncher)”提供啟動任務時需要的應用上下文

所有這些步驟將只根據傳入的參數執行直至完成。如下是必須的參數列表:

image

這些參數必須依照“路徑(path)”為先、“名稱(name)”隨后的順序傳入。所有之后的參數都被認為是“任務參數”,而且格式必須為“名稱=值”:

image

大多數時候你需要顯示申明你打包在jar文件中的main方法所在的類,但為了簡單起見,直接使用即可。例子中使用了同一個域下相同的“EndOfDay”。第一個參數是“endOfDayJob.xml”,指明了包含任務的spring“應用上下文(ApplicationContext)”。第二個參數“endOfDay”表明了任務名稱。最后的參數,“schedule.date(date)=2007/05/05”將被轉換為“任務參數(JobParameters)”。XML的配置例子如下:

image

這個例子過於簡單了,因為通常在spring batch中啟動一個任務有多得多的需要的地方,但用它來表示兩個“命令行啟動器(CommandLineJobRunner)”基本的兩項配置還是不錯的:“任務(Job)”以及“任務啟動器(JobLauncher)”。

4.4.1.2、退出碼

一般在通過命令行啟動一個批量任務時,總是使用一個企業級的調度器。大多數的調度器是在后台執行、並且僅在進程級別。這意味着它們僅僅知道操作系統進程相關的信息,比如它們調用的shell腳本。在這種場景下,告知后台調度器某個任務是成功還是失敗,只能通過任務執行后的返回碼。所謂返回碼就是指被執行的進程在最后返回給調度器的結果代碼。最簡單的情況下:0表示成功,1表示失敗。不過通常的場景比這個要復雜得多:如果任務A返回4就開始任務B,如果返回5就開始任務C。這樣類型的行為就可以再調度器級別進行配置,但重要的是當前使用的框架,比如spring batch,要提供這樣一個途徑、為某個特殊的任務返回多樣的“退出碼(Exit Code)”。在spring batch 中退出碼被包裝為“退出狀態(ExitStatus)”,而這個將在第五章節詳細討論。既然是在討論退出碼,只需要記住一件重要的事情,那就是一個“退出狀態(ExitStatus)”擁有一個被框架(或開發人員)設置的退出碼屬性,並且作為“任務執行過程(JobExecution)”的一部分返回給“任務啟動器(JobLaucher)”。“命令行任務執行器(CommandLineJobRunner)”會通過接口“退出碼映射器(ExitCodeMapper)”將字符串值轉換為整型:

image

基本的“退出碼映射器(ExitCodeMapper)”契約是這樣的,給定一個字符串退出碼,將返回對應的整型。在任務執行之后,默認的退出碼映射器接口的實現是“SimpleJvmExitCodeMapper”,當任務完成時返回0,返回1表示一般性錯誤,返回2表示任務啟動級別的錯誤、比如在提供的上下文中找不到指定的任務。如果需要更復雜的多余三種結果的返回碼,那么就要提供一個自定義實現的“退出碼映射器”。因為“命令行任務啟動器”創建了應用上下文,為了防止“裹在一起”,任何需要被覆蓋的值就需要自動裝配了。這意味着如果自定義實現了一個“退出碼映射器”並被“Bean工廠(BeanFactory)”找到,當上下文創建完成之后,它將被自動注入到啟動器中。唯一需要做的就是將你自定義的“退出碼映射器”申明為根級別的Bean接口的實現,同時確保在啟動器裝載的“應用上下文(ApplicationContext)”中,它是其中的一部分。

4.4.2、通過Web容器執行任務

通常情況下,離線的批量任務處理都是通過命令行啟動的,正如上面所討論的。但也有很多情況下,通過“頁面請求(HttpRequest)”啟動一個任務是更好的選擇。很多這樣的用例,比如報表、ad-hoc任務執行以及web應用支持。因為批量任務從定義上就決定了是一個長時間處理的過程,所以最重要的地方是一定要確保以異步的形式啟動任務:

image

圖中的“控制器(controller)”是spring MVC controller。更多spring MVC相關的內容請參考:http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html。通過配置好的“任務啟動器(JobLauncher)”,控制器可以異步地啟動一個任務,同時立即返回一個“任務執行過程(JobExecution)”。雖然任務還在執行,但是非阻塞模式允許控制器立即返回,這對於處理“頁面請求”來說至關重要。舉個例子:

image

4.5、高級的元數據用法

至此,我們討論了“任務啟動器(JobLauncher)”和“任務存儲器(JobRepository)”這兩個接口。它們一起實現了如何去啟動一個任務,如何對批量的域對象執行基本的“增刪查改(CRUD)”操作:

image

“任務啟動器(JobLauncher)”使用“任務存儲器(JobRepository)”去創建新的“任務執行器(JobExecution)”,並且運行它們。在任務執行的過程之中,“任務(Job)”和“步驟(Step)”的實現實例會在后面使用同一個“任務存儲器(JobRepository)”做簡單的更新操作。這些基本的操作只能滿足簡單的場景,如果在一個任務數以百計、並且流程需求復雜的大的批量任務環境里,更需要進一步的訪問元數據了:

image

接下來我們將討論“任務瀏覽器(JobExplorer)”和“任務操作(JobOperator)”接口,它們將在一定程度上滿足對元數據的查詢和控制。

4.5.1、查詢存儲器

在使用到任何高級特性之前,最基本的需求莫過於能夠去查詢當前存儲器中已有的執行器。“任務瀏覽器(JobExplorer)”接口提供了這樣的方法:

image

如同上面方法的簽名所示,“任務瀏覽器(JObExplorer)”是“任務存儲器(JobRepository)”的只讀版本,像后者一樣,它可以通過工廠bean簡單的配置生成:

image

在本章的早些部分,我們有提到“任務存儲器(JobRepository)”的表前綴可以更改,使得可以支持不同的版本或架構。因為“任務瀏覽器(JobExplorer)”使用相同的表,所以可以配置表前綴就極其的重要了:

image

4.5.2、任務注冊器

所謂的“任務注冊器(JobRegistry)”不是必須的(它的父接口是“任務定位器JobLocator”),但如果你想追蹤哪些任務在當前的上下文中是有效的,那么它就很有用。同時對於在一個應用上下文中集中的收集在別處創建的任務也很有用(比如在下級上下文中)。自定義實現“任務注冊器(JobRegistry)”也可以用來更改任務名稱或其他已注冊的屬性。框架只提供了一種實現,基於簡單的映射表(map):名稱,與其對應的任務實例。可以簡單的這樣配置:

image

有兩種自動構成“任務注冊器(JobRegistry)”的方法:使用bean發送處理器,以及使用“注冊生命周期管理容器”。接下來討論這兩種方法。

4.5.2.1、任務處理器的Bean發送處理器(JobRegistryBeanPostProcessor)

這是一個bean發送處理器,可以用來注冊所有已創建的任務:

image

雖然確切的說例子里給“發送處理器”分配了一個id是不必要的,但卻能通過在下級上下文指定它(自身定義為上級bean),就能將所有已創建的任務自動進行注冊。

4.5.2.2、自動任務注冊器

這是一個生命周期管理容器,用來創建下級上下文並且注冊這些上下文中創建的所有任務。這樣做的優點就是,它們的依賴項可以擁有“自然一點”的名字,但依舊必須在當前注冊器中全局唯一。舉個例子,你可以創建一大堆XML配置文件,每一個里面都只含有一個任務,但都使用同一個名字定義了不同的“元素讀取器(ItemReader)”,比如就叫“reader”。如果這些配置文件都被引入了同一個上下文,那么讀取器的定義將會沖突並覆蓋其它同名的定義,但如果通過自動注冊器,這樣的情況就不會發生。這使得集成各個分離的應用模塊到某一個任務變的簡單了。

image

注冊器含有兩個托管屬性,一個是“應用上下文工廠(ApplicationContextFactory)”(將從恰當的工廠bean創建),另一個就是“任務裝載器(JobLoader)”。任務裝載器負責管理下級上下文的生命周期以及注冊任務到任務存儲器中。

“應用上下文工廠(ApplicationContextFactory)”負責創建下級上下文,最普遍的用法就是上面看到的ClassPathXmlApplicationContextFactory。這個工廠類的一個特性就是、默認地將上一級上下文中的若干配置拷貝到下一級。如此說來,如果下一級上下文定義的PropertyPlaceholderConfigurer或AOP配置與上一級相同,那么就不需要重復定義了。

如果強烈要求的話,AutomaticJobRegistrar可以和JobRegistryBeanPostProcessor聯合起來用(就好像使用DefaultJobLoader一樣)。比如在上一級主上下文中定義的任務,同樣要用於下一級時。

4.5.3、任務操作類(JobOperator)

如前所述,“任務存儲器(JobRepository)”提供了針對元數據的“增刪查改”操作,而“任務瀏覽器(JobExplorer)”則提供了若干只讀操作。通過聯合使用諸多的批量操作類,可以完成相當多的任務控制功能,比如停止、重啟或對任務進行匯總。Spring Batch正是定義了“任務操作類(JobOperator)”這樣的接口、提供這樣的功能:

image

上圖中展示的操作重現了很多了其它接口提供的功能,比如“任務啟動類(JobLaucher)”,“任務存儲器(JobRepository)”,“任務瀏覽器(JobExplorer)”,還有“任務注冊器(JobRegistry)”。因為這個原因,所提供的“任務操作接口(JobOperator)”的實現SimpleJobOperator依賴項有很多:

image

注意:

如果你在任務存儲器中設置了表前綴,那么千萬不要忘記了在任務瀏覽器中也做同樣設置。

4.5.4、任務參數增量器(JobParametersIncrementer)

大多數“任務操作類(JobOperator)”的方法都是“自說明”的,通過接口的javadoc,你可以獲得更詳細的說明。但方法“startNextInstance”卻有些無所是處。這個方法提供任務的一個新實例。如果存在若干“任務執行過程(JobExecution)”,同時該任務需要從頭重新啟動,那么這時候這個方法就相當有用了。不像“任務啟動器(JobLauncher)”,啟動新的任務會導致需要一個新的參數實例、如果參數中有任何的不同的話,startNextInstance方式將使用當前的“任務參數增量器(JobParametersIncrementer)”綁定到這個任務,並且強制其生成新的實例:

image

“任務參數增量器”的協議是這樣的,當給定一個“任務參數”對象,它將返回“下一個”“任務參數”對象,同時填充任何可能需要的值。這個策略非常有用,因為框架無需知曉變成“下一個”任務參數實例,它做了哪些更改。比方說,如果任務參數中只包含一個日期參數,那么當創建下一個實例時,這個值就應該是不是該自增一天?或者一周(如果任務是以周圍單位運行的話)?任何包含數值類參數的任務,如果需要對其進行區分,都涉及這個問題,如下:

image

在這個例子里,鍵值“run.id”用以區分各個任務實例。如果當前的任務參數為空(null),它將被視為該任務從未運行過,並同時為其初始化,然后返回。反之,非空的時候自增一個數值,再返回。自增的數值可以通過任務的“incrementer”屬性進行設置:

image

4.5.5、停止任務

“任務操作類(JobOperator)”最常見的作用莫過於停止某個任務:

image

關閉不是立即發生的,因為根本不可能將一個任務立刻停掉,尤其是當任務進行到開發人員自己的代碼段時,框架在此刻是無能為力的,比如某個業務邏輯處理。而一旦控制權還給了框架,它會立刻設置當前“執行步驟(StepExecution)”為“BachStatus.STOPPED”,意為停止,然后保存,最后在完成前對“任務執行器(JobExecution)”做相同的操作。

4.5.6、取消任務

一個任務的執行過程當執行到失敗(FAILED)之后,如果它是可重啟的,它將會被重啟。如果任務的執行過程狀態是“取消(ABORTED)”,那么框架就不會重啟它。“取消”狀態也適用於執行步驟,使得它們可以被跳過,即便是在一個可重啟的任務執行之中:如果任務執行過程中碰到在上一次執行失敗后標記為“取消”的步驟,將會跳過該步驟直接到下一步(這是由任務流定義和執行步驟的退出碼決定的)。

如果當前的系統進程死掉了(“kill -9”或系統錯誤),任務自然也不在運行,但“任務存儲器(JobRepository)”是無法偵測到這個錯誤的,因為進程死掉之前沒有任何人通知它。你必須手動的告訴它,你知道任務已經失敗了還是說考慮放棄這個任務(設置它的狀態為FAILED或ABORTED)-這是業務邏輯層的事情,無法做到自動決策。只有在不可重啟的任務重才需要設置為失敗狀態,或者你知道重啟后數據還是有效的。Spring Batch Admin中有一系列工具“任務服務(JobService)”,用以取消正在進行的執行的任務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM