Java EE 7 - Batch Applications for the Java Platform(一) by kapa | CodeData
top

Java EE 7 - Batch Applications for the Java Platform(一)

分享:

Java EE 7 介紹 << 前情

簡介

在 Java EE 7 系列中第一個要介紹的新功能,就是 JSR-352 的 Batch Applications for the Java Platform。在各種企業、部門都有可能會遇到需要大批資料處理的狀況,例如 Log 處理、庫存統計、薪資試算、交易紀錄整理…等等,這些都有可能是批次處理的對象。這些工作可能是輸入資料量大、也可能是需要大量的處理資源運算;可能會是週期性的工作,也可能會是手動不定時觸發;可能適合同時多工作並行,也可能工作間存在先後次序的相依關係。

可以發現批次處理的常見特徵為需要處理時間長,資料量大,過程中不太需要人為操作互動,可放在背景執行後等待完成。在傳統上批次處理系統會聽到 COBOL、JCL。如果是 Java 工程師則可能會使用 Spring Batch 或是其他框架來實現。

基本上 JSR-352 採用了許多批次處理系統的常用架構、定義和詞彙,如果已經用過一些常見的批次處理相關框架,尤其像看過 Spring Batch,那麼對 JSR-352 的撰寫和設定方式可能不會感到陌生。

JSR-352: Batch Applications for the Java Platform

下圖為 JSR-352 的架構,也是近年來批次處理系統的典型架構,簡單呈現了一個批次處理所需要的元件。一個 Job 會有多個 Step,每個 Step 會各有一種 Reader、 Processor 和 Writer。 Job 透過 JobOperator 啟動,而執行過程中和結束的資訊則儲存在 JobRespository。

Screenshot_from_2013-09-23 00:44:25

上面提到每個 Step 會各有一種 Reader、 Processor 和 Writer,這是稱作 Chunk Oriented Processing 方式,一次讀入一筆資料後處理,處理完畢後累積部份(Chunks)資料後在一次輸出,在後面的範例程式便可以了解 ItemReader、 ItrmProcessor 和 ItemWriter 就是分別做上述的事情。

可以注意到的是,JSR-352 規格中並未定義像 cron 的工作排程機制,也就是說為了要啟動批次處理的應用程式,你可能需要搭配 cron 自動或是人為主動的方式。

JSR-352 中對於 job 的設定方式是透過 XML,以下是一個簡易的例子。

<job id="simpleJob">
    <step id="step1" next="step2">...</step>
    <step id="step2">...</step>
</job>

上面的定義為有一個 job 名稱為 simpleJob,他總共有兩個 Step,其中 step1 執行完成後便會執行 step2。例如我們撰寫一個每日交易的處理程式,整個工作就是一個 job,其中第一步(step1)是將今天的交易從資料庫取出後,將它們依照每日指定時間的匯率,轉換為特定金額後寫入檔案。等所有資料都處理完畢後,再開始通知廠商結果金額和庫存狀況(step2)。

以上述例子來說,其實這類應用運作原理並不難,對於一個工程師來說,要撰寫一個可以馬上執行的程式並不難;但是要能處理各種例外狀況,保持程式長時間運作良好,或是未來能因應需求可以彈性修改流程,就不是一件簡單的事情。

例如上述應用可能在執行中遇到有問題的訂單資料、連不上外部系統取得匯率或是當日訂單,或是重新啟動時重複浪費時間計算已經執行完成的資料。對於一個有經驗的工程師來說,這些可能也不是太大問題,但是為了解決或達到上述目標,建立一些基本的框架或是函式是不可避免的。而 JSR-352 便是將這些業界已經常用的架構標準化,利用實作的 Java EE 執行環境,讓我們可以直接專注於應用本身的邏輯,而不需要自行從頭開始建立框架。

JSR-352中最主要的功能有以下四點,分別為「流程控制」、「Checkpoint」、「例外處理」和「平行處理」。

流程控制

在做各種不同目標的批次處理時,通常不會只有一個單純的資料輸入,處理完畢後輸出就結束。過程中常常會有多個步驟,步驟間彼此又不同的相依狀態,也有可能依照條件狀況有不同的執行順序。因此在 JSR-352 中便定義了幾種不同方式(Flow、Split 和 Decision)來讓我們在撰寫 job xml 時能達到上述所提的流程控制。

Flow 是將一群 Step 包成一個運作單位的方法,尤其遇到多個 Step 時,對於流程控制會有幫助。Split 則是用來定義可以並行計算的多個不同 Flow,來善用多執行緒的優勢來執行多個可同時執行的工作。 Decison 則是可以依照 Flow 執行狀況決定接下來要執行的 Flow 或結束。

Checkpoint

需要長時間運作的程式,最怕的就是執行失敗後必須重來,如果是秒到分鐘單位的運算,可能不會讓人覺得麻煩,但如果一個工作在執行小時為單位後必須重來,那麼就真的是浪費時間了。為了解決此問題,我們通常會將運作的過程,一個階段一個階段儲存起來,就算過程中發生意外,我們也可以從最後執行到的資料開始接著,而不需要重頭開始。JSR-352 也提供了此功能,尤其 Chunk Oriented processing 更是適合此類機制。因為每次處理完一個 chunk 便是一個很好的 checkpoint。

例外處理

在做批次處理時最常見的幾個問題,有像是輸入的資料中有部份格式不對;資料取得來源或輸出對象暫時無法連上,可能一段時間後就可以使用;或是嚴重的錯誤像是沒有權限對檔案做操作、硬碟已滿造成資料無法寫入的狀況。

對應不同的錯誤類型就需要不同的處理方式,例如輸入的資料中含有錯誤資料並不表示需要中止,有時只需跳過該筆資料即可。而操作中發生暫時性的錯誤狀況,則可以等待一段時間後在重試。如果是其他須人工介入的錯誤,則在人工處理完後重新啟動。

JSR-352 便提供了對應的功能來達到上述的錯誤處理方式。例如在設定檔中定義要略過或重試的錯誤類別,當偵測到對應的錯誤類別便跳過該筆資料或重試,或是當遇到需要手動處理的錯誤時,重新啟動後是否要讓已經完成的工作再執行一次。

平行處理

與流程控制中的 split 不同, split 是讓多個不同的 flow,可以分配到多個執行緒執行;JSR-352 另外提供了一種稱為 step partitioning 的方式,它是讓一個 Step 可以產生多個實例分配到不同執行緒執行。

當然每個實例都必須依照設定靜態或動態方式,決定要處理的資料範圍。靜態方式可以透過 job xml 定義,而動態方式則有提供  PartitionMapper 來讓我們實作。例如電子商務網站會週期性舉辦活動的關係而讓交易量爆增,為了善用機器的資源和縮短處理時間,我們可以將原本定義的 step,依照當日的筆數和機器的處理能力切割成複數份執行。

第一個批次處理

筆者的環境是:

  • NetBeans 7.3.1
  • GlassFish Server 4.0

只要在安裝NetBeans時勾選一起安裝GlassFish就可以準備好開發環境。

1. 新增專案:選擇工具列的 New Project -> Java EE -> Enterprise Application

2. 先建立一個文字檔當作輸入來源,在自己偏好的目錄下建立一個 input.txt 內容如下,我們的第一個範例程式,要將每列資料的第二行加上7%當作輸出。

A1,1000,2
A2,2000,3
B2,810,1
.
.

3. 在 Source Packages 新增一個 Reader Java Class,此類別需繼承 AbstractItemReader。這個批次處理程式的輸入就從系統的檔案讀取每一筆資料,並將資料的第二行當作真正要處理的輸入。ItemReader 有 open 跟 close 讓我們處理資源的開關;readItem 方法處理輸入的資料,回傳 null 時代表資料讀取結束。

@Named
@Dependent
public class SimpleJobReader extends AbstractItemReader 
{

    private BufferedReader inputReader;    

    @Override
    public void open(Serializable checkpoint) throws Exception {
        inputReader = new BufferedReader(new FileReader("/tmp/input.txt"));
    }

    @Override
    public void close() throws Exception {
        inputReader.close();
    }
    @Override
    public Object readItem() throws Exception {
        String line = inputReader.readLine();
        if(line == null) {
              return null;
        }
        String[] fields = line.split(",");
        return fields[1];
    }
}

4. 同目錄下新增 Processor Java Class,此類別需實作 ItemProcessor,所有在 SimpleJobReader 讀的資料都會進入到此類別的 processItem 方法,在這邊我們將每筆資料乘上1.07並四捨五入。

@Named
@Dependent
public class SimpleJobProcessor implements ItemProcessor 
{
    @Override
    public Object processItem(Object item) {
        final String record = (String) item;
        final BigDecimal price = new BigDecimal(record);
        final BigDecimal withTax = price.multiply(
                  new BigDecimal("1.07")).setScale(0, RoundingMode.HALF_UP); 
        return withTax;
    }
}

5. 同目錄下新增 Writer Java Class,此類別需繼承 AbstractItemWriter,在這邊我們將處理完的資料寫到指定的檔案中。

@Named
@Dependent
public class SimpleJobWriter extends AbstractItemWriter 
{
    private BufferedWriter output;

    @Override
    public void open(Serializable checkpoint) throws Exception {
        output = new BufferedWriter(new FileWriter("/tmp/output.txt"));
    }

    @Override
    public void close() throws Exception {
        output.close();
    }
    @Override
    public void writeItems(List<Object> items) {
        for (Object data : items) {
            BigDecimal withTax = (BigDecimal) data;
            output.write(withTax.toString());
        }
    }
}

6. 在 Source Packages 下新增 META-INF/batch-jobs 目錄後新增一個 simple-job.xml。

<?xml version="1.0" encoding="UTF-8"?>
<job id="simplejob" xmlns="http://xmlns.jcp.org/xml/ns/javaee"version="1.0">
    <step id="step1">
        <chunk>
            <reader ref="simpleJobReader"/>
            <processor ref="simpleJobProcessor"/>
            <writer ref="simpleJobWriter"/>
        </chunk>
    </step>
</job>

7. 新增一個 Servlet 來啟動批次處理,透過 NetBeans 新增Servlet後在processRequest內新增啟動用的程式碼。

JobOperator jobOperator = BatchRuntime.getJobOperator();
Properties props = new Properties();
jobOperator.start("simple-job", props);

8. 執行步驟6的servlet後可以到目錄內觀看已經完成的檔案,也可以到 GlassFish 後台 http://localhost:4848,點選左邊"伺服器"分類後的"批次"分頁瀏覽批次處理的執行結果。

Screenshot_from_2013-09-27 01:22:48

結尾

本次介紹了JSR-352的架構,和其中較重要的功能以及一個簡單的翻例,下一次將會針對這些功能簡單介紹幾個範例。

後續 >> Java EE 7 – Batch Applications for the Java Platform(二)

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

關於作者

愛好 Java 的工程師,前幾年主要開發 Web Application,最近則在接觸 Mobile App 開發。

熱門論壇文章

熱門技術文章