java与java ee
Mahesh Kannan研究了JSR 352为Java EE 7提供的新批处理功能。该文件经Oracle公司Oracle技术网许可转载。
批处理在许多行业中用于从工资单处理到任务处理的整个过程。 语句生成; 数据仓库中的日常工作,例如利息计算和ETL(提取,加载和转换); 还有很多。 通常,批处理是面向批量的,非交互的且运行时间长的,并且可能是数据或计算密集型的。 批处理作业可以按计划运行或按需启动。 另外,由于批处理作业通常是长时间运行的作业,因此检查点和重新启动是批处理作业中的常见功能。
JSR 352 (Java平台的批处理)是最近推出的Java EE 7平台的一部分,它定义了批处理应用程序的编程模型以及运行和管理批处理作业的运行时。 本文介绍了一些关键概念,包括功能亮点,所选API的概述,作业规范语言的结构以及示例批处理应用程序。 本文还介绍了如何使用GlassFish Server Open Source Edition 4.0运行批处理应用程序。
批处理架构
本节和图1描述了批处理架构的基本组件。
- 作业封装了整个批处理过程。 一项作业包含一个或多个步骤。 使用作业指定语言(JSL)将作业放在一起,该作业指定语言指定必须执行步骤的顺序。 在JSR 352中,JSL在称为作业XML文件的XML文件中指定。 简而言之,一项工作(使用JSR 352)基本上是步骤的容器。
- 步骤是一个域对象,它封装了作业的一个独立的顺序阶段。 步骤包含执行实际处理所需的所有必要逻辑和数据。 批处理规范故意使步骤的定义含糊不清,因为步骤的内容完全是特定于应用程序的,并且可以像开发人员所希望的那样复杂或简单。 有两种步骤:块和批处理。
- 块式步骤仅包含一个
ItemReader
,一个ItemProcessor
和一个ItemWriter
。 在这种模式下,ItemReader
读取一个项目,ItemProcessor
根据业务逻辑(例如“计算帐户余额”)处理该项目,并将其交给批处理运行时进行汇总。 一旦读取并处理了“大块大小”数量的项目,它们就会被提供给ItemWriter
,后者会写入数据(例如,写入数据库表或平面文件)。 然后提交事务。 - JSR 352还定义了一个自己的步骤,称为Batchlet 。 批处理程序可以自由使用任何东西来完成该步骤,例如发送电子邮件。
- 块式步骤仅包含一个
-
JobOperator
提供了一个界面来管理作业处理的所有方面,包括操作命令(例如开始,重新启动和停止)以及作业存储库命令(例如,检索作业和步骤执行)。 有关JobOperator
更多详细信息,请参见JSR 352规范的10.4节。 -
JobRepository
包含有关当前正在运行的作业和过去运行的作业的信息。JobOperator
提供用于访问此存储库的API。JobRepository
可以使用数据库或文件系统来实现JobRepository
。
开发一个简单的薪资处理应用程序
本文通过一个简单的薪资处理应用程序演示了JSR 352的一些关键功能。 故意将应用程序保持为非常简单,以便专注于JSR 352的关键概念。
SimplePayrollJob
批处理作业涉及从逗号分隔值(CSV)文件读取用于薪资处理的输入数据。 文件中的每一行都包含一个员工ID和一个员工的基本工资(每月)。 然后,批处理作业将计算要预扣的税金,奖金和净工资。 最后,作业需要将处理后的工资记录写出到数据库表中。
在此示例中,我们使用CSV文件只是为了证明JSR 352允许批处理应用程序从任何任意源进行读写。
薪资处理应用程序的作业规范语言
我们讨论过,步骤是一个域对象,它封装了作业的一个独立的顺序阶段,而作业基本上是一个或多个步骤的容器。
在JSR 352中,JSL基本上指定了为了完成工作而必须执行步骤的顺序。 JSL足够强大,可以有条件地执行步骤,并且还允许每个步骤都有其自己的属性,侦听器等。
批处理应用程序可以根据需要具有任意数量的JSL,从而允许它启动所需的任意多个批处理作业。 例如,一个应用程序可以有两个JSL,一个用于工资单处理,另一个用于报表生成。 每个JSL必须唯一地命名,并且必须放置在META-INF/batch-jobs
目录中。 META-INF/batch-jobs
下的子目录将被忽略。
我们用于工资单处理的JSL放在一个名为SimplePayrollJob.xm
l的文件中,如清单1所示 :
<job id="SimplePayrollJob" xmlns=http://xmlns.jcp.org/xml/ns/javaee version="1.0">
<step id="process">
<chunk item-count="2">
<reader ref="simpleItemReader/>
<processor ref="simpleItemProcessor/>
<writer ref="simpleItemWriter/>
</chunk>
</step>
</job>
我们的SimplePayrollJob
批处理作业只有一个步骤(称为“过程”)。 它是一个块样式步骤,并具有(按块样式步骤的要求), ItemReader
, ItemProcessor
和ItemWriter
。 使用<reader>
, <processor>
和<writer>
元素中的ref
属性指定此步骤的ItemReader
, ItemProcessor
和ItemWriter
实现。
提交作业后(我们将在后面看到如何提交批处理作业),批处理运行时从JSL的第一步开始,逐步进行直到整个作业完成或其中一个步骤失败。 JSL的功能足以允许有条件的步骤和步骤的并行执行,但是我们将不在本文中介绍这些细节。
清单1中定义为2
的item-count
属性定义了块的块大小。
这是如何执行块样式步骤的高级概述。 有关更多详细信息,请参见JSR 352规范的“常规块处理”部分。
- 开始交易。
- 调用
ItemReader
并通过由读取项目ItemReader
到ItemProcessor
。ItemProcessor
处理项目,并将处理后的项目返回到批处理运行时。 - 批处理运行时重复步骤2的
item-count
时间,并维护已处理项目的列表。 - 批处理运行时调用
ItemWriter
写入item-count
处理项目数。 - 如果从
ItemReader
,ItemProcessor
或ItemWriter
引发异常,则事务将失败,并且该步骤被标记为“ FAILED”。 请参考JSR 352规范中的第5.2.1.2.1节(“跳过异常”)。 - 如果没有异常,则批处理运行时将从
ItemReader
和ItemWriter
获取检查点数据(有关更多详细信息,请参见JSR 352规范中的2.5节)。 批处理运行时将提交事务。 - 如果
ItemReader
有更多数据要读取,则重复步骤1至6。
这意味着在我们的示例中,批处理运行时将读取和处理两条记录,而ItemWriter
将为每个事务写出两条记录。
编写ItemReader,ItemProcessor和ItemWriter
编写ItemReader
我们的工资单处理一批JSL定义了一个块风格步,并指定步骤使用ItemReader
命名simpleItemReader
。 我们的应用程序包含ItemReader
的实现,以读取输入的CSV数据。 清单2显示了我们的ItemReader
的代码片段:
@Named
public class SimpleItemReader
extends AbstractItemReader {
@Inject
private JobContext jobContext;
...
}
请注意,该类使用@Named
注释进行注释。 因为@Named
批注使用默认值,所以此bean的上下文和依赖注入(CDI)名称为simpleItemReader
。 JSL在<reader>
元素中指定ItemReader
的CDI名称。 这使批处理运行时在执行步骤时实例化(通过CDI)我们的ItemReader
。
我们的ItemReader
还注入一个JobContext
。 JobContext
允许批处理工件(在本例中为ItemReader
)读取作业提交期间传递的值。
我们的工资单SimpleItemReader
重写open()
方法以打开从中读取工资单输入数据的输入。 稍后我们将看到,如果作业正在重新启动,则参数prevCheckpointInf
o将不会为null。
在我们的示例中, 清单3所示的open()
方法将打开工资输入文件(已与应用程序打包在一起)。
public void open(Serializable prevCheckpointInfo) throws Exception {
JobOperator jobOperator = BatchRuntime.getJobOperator();
Properties jobParameters = jobOperator.getParameters(jobContext.getExecutionId());
String resourceName = (String) jobParameters.get("payrollInputDataFileName");
inputStream = new FileInputStream(resourceName);
br = new BufferedReader(new InputStreamReader(inputStream));
if (prevCheckpointInfo != null)
recordNumber = (Integer) prevCheckpointInfo;
for (int i=1; i<recordNumber; i++) { //Skip upto recordNumber
br.readLine();
}
System.out.println("[SimpleItemReader] Opened Payroll file for reading from record number: " + recordNumber);
}
所述 readItem()
方法基本上读取从输入文件数据的一行,并确定行是否包含两个整数(一个用于雇员ID,一个用于基本工资)。 如果有两个整数,它将创建并返回一个新的 PayrollInputRecord
实例, 并返回到批处理运行时(然后将其传递给 ItemWriter
)。
清单4
public Object readItem() throws Exception {
Object record = null;
if (line != null) {
String[] fields = line.split("[, trn]+");
PayrollInputRecord payrollInputRecord = new PayrollInputRecord();
payrollInputRecord.setId(Integer.parseInt(fields[0]));
payrollInputRecord.setBaseSalary(Integer.parseInt(fields[1]));
record = payrollInputRecord;
//Now that we could successfully read, Increment the record number
recordNumber++;
}
return record;
}
在每个成功的块事务结束时,批处理运行时都会调用方法checkpointInfo()
。 这使阅读器可以检查点最后一次成功的读取位置。
在我们的示例中, checkpointInfo()
返回recordNumber
指示已成功读取的记录数,如清单5所示。
@Override
public Serializable checkpointInfo() throws Exception {
return recordNumber;
}
编写ItemProcessor
我们的
processItem()
方法(从批处理运行时)接收PayrollInputRecord
。 然后,它计算税额和净额,并返回PayrollRecord
作为输出。 请注意,在清单6中 ,由ItemProcessor
返回的对象的类型可能与从ItemReader
接收到的对象的类型非常不同。
@Named
public class SimpleItemProcessor
implements ItemProcessor {
@Inject
private JobContext jobContext;
public Object processItem(Object obj)
throws Exception {
PayrollInputRecord inputRecord =
(PayrollInputRecord) obj;
PayrollRecord payrollRecord =
new PayrollRecord();
int base = inputRecord.getBaseSalary();
float tax = base * 27 / 100.0f;
float bonus = base * 15 / 100.0f;
payrollRecord.setEmpID(inputRecord.getId());
payrollRecord.setBase(base);
payrollRecord.setTax(tax);
payrollRecord.setBonus(bonus);
payrollRecord.setNet(base + bonus - tax);
return payrollRecord;
}
}
编写ItemWriter
到目前为止, SimpleItemWriter
必须为您遵循可预测的行。
唯一的区别是,它注入了EntityManager
以便可以将PayrollRecord
实例(它们是JPA实体)持久PayrollRecord
到数据库中,如清单7所示。
@Named
public class SimpleItemWriter
extends AbstractItemWriter {
@PersistenceContext
EntityManager em;
public void writeItems(List list) throws Exception {
for (Object obj : list) {
System.out.println("PayrollRecord: " + obj);
em.persist(obj);
}
}
}
writeItems()
方法使用JPA将所有PayrollRecord
实例持久PayrollRecord
到数据库表中。 列表中最多有item-count
条目(块大小)。
现在我们已经准备好了JSL, ItemReader
, ItemProcessor
和ItemWriter
,让我们看看如何提交批处理作业。
从Servlet启动批处理作业
请注意,仅存在作业XML文件或其他批处理工件(例如ItemReader
)并不意味着在部署应用程序时自动启动批处理作业。 批处理作业必须显式启动,例如,从servlet或从Enterprise JavaBeans(EJB)计时器或EJB业务方法启动。
在我们的薪资应用程序中,我们使用servlet(名为PayrollJobSubmitterServlet
)提交批处理作业。 Servlet显示一个HTML页面,该页面向用户提供一个包含两个按钮的表单。 单击标记为Calculate Payroll的第一个按钮时,该servlet调用startNewBatchJob
方法,如清单8所示,该方法将启动一个新的批处理作业。
private long startNewBatchJob()
throws Exception {
JobOperator jobOperator = BatchRuntime.getJobOperator();
Properties props = new Properties();
props.setProperty("payrollInputDataFileName", payrollInputDataFileName);
return jobOperator.start(JOB_NAME, props);
}
第一步是获取JobOperator
的实例。 可以通过调用以下命令来完成:
JobOperator jobOperator = BatchRuntime.getJobOperator();
然后,该Servlet创建一个Properties
对象,并将输入文件名存储在其中。 最后,通过调用以下命令来启动新的批处理作业:
jobOperator.start(jobName, properties)
jobname
名称不过是作业JSL XML文件名(减去.xml
扩展名)。 properties
参数用于将任何输入数据传递给作业。 通过JobContext
接口, Properties
对象(包含工资输入文件的名称)可用于其他批处理工件(如ItemReader
, ItemProcessor
等)。
批处理运行时分配一个唯一的ID,称为执行ID,以标识作业的每次执行,无论是新提交的作业还是重新启动的作业。 许多JobOperator
方法将执行ID作为参数。 使用执行ID,程序可以获取当前(和过去)执行状态以及有关作业的其他统计信息。 JobOperator.start()
方法返回已启动作业的执行ID。
检索有关批处理作业的详细信息
提交批处理作业后,批处理运行时将创建JobExecution
实例以对其进行跟踪。 JobExecution
具有获取各种详细信息的方法,例如作业开始时间,作业完成时间,作业退出状态等。 为了获得JobExecution
用于执行ID,则可以使用JobOperator.getJobExecution(executionId)
方法。 清单9显示了JobExecution
的定义:
package javax.batch.runtime;
public interface JobExecution {
long getExecutionId();
java.lang.String getJobName();
javax.batch.runtime.BatchStatus getBatchStatus();
java.util.Date getStartTime();
java.util.Date getEndTime();
java.lang.String getExitStatus();
java.util.Date getCreateTime();
java.util.Date getLastUpdatedTime();
java.util.Properties getJobParameters();
}
打包应用程序
现在我们已经准备好了JSL, ItemReader
, ItemProcessor
, ItemWriter
和servlet,现在该打包它们并准备部署了。
您可以将批处理应用程序部署为任何受支持的Java EE归档文件(例如.war
, .jar
或.ear
)。 您可以将批处理工件类与其他Java EE类(例如EJB bean和servlet)捆绑在一起。
唯一的特殊要求是,您需要将作业JSL放在.jar
文件的META-INF/batch-jobs
目录下。 对于.war
存档类型,请将您的作业JSL放在WEB-INF/classes/META-INF/batch-jobs
目录下。
在Glassfish 4.0中部署和运行薪资示例应用程序
让我们将我们开发的工资单应用程序部署到GlassFish 4.0应用程序服务器中。 GlassFish 4.0是Java EE 7.0规范的参考实现(RI),并且还包含JSR 352的RI。 您可以在http://glassfish.org上找到有关GlassFish 4.0的更多信息,并在https://java.net/projects/jbatch/上找到有关Java Batch 1.0 RI的更多信息。
安装和启动GlassFish 4.0
您可以从https://glassfish.java.net/download.html下载GlassFish 4.0,然后进行安装。 通过打开命令窗口并运行以下命令来启动GlassFish 4.0:
<GlassFish Install Dir>/bin/asadmin start-domain
因为示例薪资应用程序使用数据库(写出处理过的数据),所以我们需要先运行数据库,然后才能运行我们的应用程序。 您可以通过运行以下命令来启动Apache Derby数据库:
<GlassFish Install Dir>/bin/asadmin start-database
编译,打包和部署薪资应用程序
首先,创建一个名为hello-batch
的新目录。 然后转到hello-batch
目录:
cd hello-batch
要编译和打包,请运行以下命令,该命令将在目标目录下创建hello-batch.war
:
mvn clean package
要部署hello-batch.war
,请运行以下命令:
<GlassFish Install Dir>/bin/asadmin deploy target/hello-batch.war
如果要重新部署应用程序,可以运行以下命令:
<GlassFish Install Dir>/bin/asadmin deploy -force target/hello-batch.war
运行工资核算应用程序
部署hello-batch.war
文件后,您可以通过从浏览器访问http://localhost:8080/hello-batch/PayrollJobSubmitterServlet
来运行该应用程序。 访问该URL将显示如图2所示的屏幕。
单击Calculate Payroll按钮,您应该在表中看到一个新条目,如图3所示。
单击Refresh按钮,您应该看到为最新作业而更新的Exit Status和End Time列(请参见图4)。 “退出状态”列显示作业是失败还是成功完成。 由于我们的SimplePayrollJob
没有任何错误(至少现在还没有!),因此退出状态显示为COMPLETED。
再单击几次“ 计算工资和刷新”按钮。 请注意,每次启动作业时,都会为该作业分配一个新的执行ID(和实例ID),如图5所示。
到目前为止,我们一直在使用jobOperator.start()
方法启动批处理作业。 假设我们的工资单输入文件有一些错误。 ItemReader
或ItemProcessor
都可以检测到无效记录并使当前步骤和作业失败。 管理员或最终用户可以纠正错误并可以重新启动批处理作业。 如果要处理的数据量很大,这种从错误中恢复后从头开始启动新作业的方法可能无法扩展。 JobOperator
提供了另一个名为restart()
方法来完全解决此问题。
JobInstance和JobExecution快速概述
前面我们看到,工作本质上是步骤的容器。 启动作业时,必须对其进行跟踪,因此批处理运行时将创建JobInstance
。 JobInstance
指逻辑运行的概念。 在我们的示例中,我们有一个PayrollJob
,如果PayrollJob
运行一次PayrollJob
,将有一个Jan-2013 JobInstance
,还有另一个Feb-2013 JobInstance
,依此类推。
如果2013年1月的工资单处理失败,则必须重新启动它(大概是在纠正了错误之后),但由于它仍在处理2013年1月的记录,因此它仍是2013年1月运行。
JobExecution
是指一次尝试运行Job的概念。 每个工作启动或重新启动时,一个新的JobExecution
创建属于同一JobInstance
。 在我们的例子中,如果扬2013 JobInstance
重新启动,它仍然是相同的扬2013 JobInstance
而是一个新的JobExecution
创建属于同一JobInstance
。
总之,一个作业可以具有一个或多个JobInstance
实例,而每个JobInstance
可以具有一个或多个JobExecution
实例。 使用新的JobInstance
意味着“从头开始”,而使用现有的JobInstance
通常意味着“从JobInstance
开始的地方开始”。
恢复失败的作业
如果您还记得的话,将在事务中执行块式步骤,在该事务中将读取,处理和写入item-count
条目。 在调用ItemWriter
的writeItems()
之后,批处理运行时将在ItemReader
和ItemWriter
上调用checkpointInfo()
方法。 这允许ItemReader
和ItemWriter
都将其当前进度ItemWriter
为书签(保存)。 为ItemReader
书签的数据可以是任何有助于其恢复读取的数据。 例如,我们的SimpleItemReader
需要保存到目前为止已成功读取的行号。
JSR 352规范的10.8节详细描述了重新启动处理。
让我们花点时间看一下日志文件,其中我们的SimpleItemReader
从open()
和checkpoint()
方法输出一些有用的消息。 每个消息都以字符串[SimpleItemReader]
为前缀,因此您可以快速识别消息。 日志文件位于<GlassFish install Dir>/domains/domain1/logs/server.log
。
清单10显示了以字符串[SimpleItemReader]
为前缀的消息:
[SimpleItemReader] Opened Payroll File. Will start reading from record number: 0]]
[SimpleItemReader] checkpointInfo() called. Returning current recordNumber: 2]]
[SimpleItemReader] checkpointInfo() called. Returning current recordNumber: 4]]
[SimpleItemReader] checkpointInfo() called. Returning current recordNumber: 6]]
[SimpleItemReader] checkpointInfo() called. Returning current recordNumber: 8]]
[SimpleItemReader] checkpointInfo() called. Returning current recordNumber: 9]]
[SimpleItemReader] close called.]]
注意 :您也可以使用命令tail -f server.log | grep SimpleItemReader
tail -f server.log | grep SimpleItemReader
。
因为我们的作业XML文件( SimplePayrollJob.xml
)将item-count
的值指定为2
作为块大小,所以批处理运行时每两个记录在ItemReader
上调用一次checkpointInfo()
。 批处理运行时将此检查点信息存储在JobRepository
。 因此,如果在我们的块处理期间发生错误,则批处理应用程序必须能够从上一个成功的检查点恢复。
让我们在输入数据文件中引入一些错误,并了解如何从输入错误中恢复。
如果查看位于<GlassFish install Dir>/domains/domain1/applications/hello-batch/WEB-INF/classes/payroll-data/payroll-data.csv
下的servlet输出,您会看到它显示了输入文件的位置,从中读取薪水应用程序的CSV数据。 清单11显示了文件的内容:
1, 8100
2, 8200
3, 8300
4, 8400
5, 8500
6, 8600
7, 8700
8, 8800
9, 8900
打开您喜欢的编辑器并引入错误。 例如,假设我们在第八条记录的薪水字段中添加了几个字符,如清单12所示:
1, 8100
2, 8200
3, 8300
4, 8400
5, 8500
6, 8600
7, 87008, abc8800
9, 8900
保存文件并退出编辑器。 返回浏览器并单击“ 计算工资” 按钮,然后 单击 “ 刷新” 按钮。 您会看到最近提交的作业失败,如图6所示。(请查看“退出状态”列。)
您还会注意到,刚失败的作业的执行ID旁会出现一个“ 重新启动”按钮。 如果您单击刷新 ,则作业将失败(因为我们尚未解决问题)。 图7显示了单击“ 刷新”按钮后显示的内容。
如果查看GlassFish服务器日志(位于<GlassFish install Dir>/domains/domain1/logs/server.log
),您将看到一个异常,如清单13所示:
Caught exception executing step: com.ibm.jbatch.container.exception.BatchContainerRuntimeException:
Failure in Read-Process-Write Loop
...
...Caused by: java.lang.NumberFormatException: For input string: "abc8800"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:492)
at java.lang.Integer.parseInt(Integer.java:527)
at com.oracle.javaee7.samples.batch.hello.SimpleItemReader.readItem(SimpleItemReader.java:100)
您还应该注意到,当您单击重新启动按钮时,将创建一个新的作业执行,但其作业实例ID保持不变。 当您单击Refresh按钮时,我们的PayrollJobSubmitter
servlet调用一个名为restartBatchJob()
的方法,如清单14所示:
private long restartBatchJob(long lastExecutionId)
throws Exception {
JobOperator jobOperator = BatchRuntime.getJobOperator();
Properties props = new Properties();
props.setProperty("payrollInputDataFileName", payrollInputDataFileName);return jobOperator.restart(lastExecutionId, props);
}
清单14中的关键行是对JobOperator
的restart()
方法的调用。 此方法采用与start()
类似的Properties
对象,但不传递作业XML文件名,而是传递最近失败的作业的执行ID。 使用最近失败的作业的执行ID,批处理运行时可以检索前一次执行的最后成功检查点。 检索到的检查点数据将传递到我们的SimpleItemReader
(和ItemWriter
)的open()
方法,以使它们能够从上一个成功的检查点恢复读取(和写入)。
在确保浏览器显示带有“ 重新启动”按钮的页面的同时,再次编辑文件并从第八条记录中删除多余的字符。 然后单击重新启动和刷新按钮。 最新执行应显示COMPLETED状态, 如图8所示。
现在是时候查看日志文件以了解刚刚发生了什么。 再次,查找带有SimpleItemReader
前缀的消息, 清单15显示了您可能看到的内容:
[SimpleItemReader] Opened Payroll File. Will start reading from record number: 7]]
[SimpleItemReader] checkpointInfo() called. Returning current recordNumber: 9]]
[SimpleItemReader] checkpointInfo() called. Returning current recordNumber: 10]]
[SimpleItemReader] close called.]]
如您所见,我们的SimpleItemReader
的open()
方法是使用先前的检查点值(记录号为7) SimpleItemReader
,从而使我们的SimpleItemReader
可以跳过前六个记录并从第七个记录恢复读取。
使用GlassFish 4.0管理控制台查看批处理作业
您可以在JobRepository
查看所有批处理作业的JobRepository
。 启动浏览器窗口,然后转到localhost:4848
。 然后单击左侧面板中的服务器(Admin Server) , 如图9所示。
您可以单击“ 批处理”选项卡,其中应列出所有提交给此GlassFish服务器的批处理作业。 请注意, JobRepository
是使用数据库实现的,因此,作业详细信息可以在GlassFish 4.0服务器重新启动后继续存在。 图10显示了JobRepository
中的所有批处理作业。
您也可以单击“执行ID”下列出的ID之一。 例如,单击293将显示有关该执行的详细信息:
通过单击顶部的“ 执行步骤”选项卡可以获得有关执行的更多详细信息。
查看此页面提供的统计信息。 它显示了在此执行期间执行了多少次读取,写入和提交。
使用GlassFish 4.0 CLI查看批处理作业
您还可以使用命令行界面(CLI)查看有关GlassFish 4.0服务器中运行的作业的详细信息。
要查看批处理作业的列表,请打开命令窗口并运行以下命令:
asadmin list-batch-jobs -l
您应该看到类似于图13的输出:
要查看批处理JobExecution
的列表,可以运行以下命令:
asadmin list-batch-job-executions -l
您应该看到类似于图14的输出:
该命令列出了每个执行的完成状态以及传递给每个执行的作业参数。
最后,为了查看有关JobExecution
每个步骤的详细信息,可以使用以下命令:
asadmin list-batch-job-steps -l
您应该看到类似于图15的输出:
记下STEPMETRICS列。 它告诉了ItemReader
和ItemWriter
被调用了多少次,以及ItemWriter
了多少次提交和回滚。 这些是非常有价值的指标。
CLI输出必须与管理控制台视图匹配,因为它们都查询相同的JobRepository
。
您可以使用asadmin help <command-name>
获取有关CLI命令的更多详细信息。
结论
在本文中,我们看到了如何编写,打包和运行使用块式步骤的简单批处理应用程序。 我们还看到了批处理运行时的检查点功能如何轻松重启失败的批处理作业。 但是,我们几乎没有刮过JSR 352的表面。有了全套Java EE组件和功能,包括servlet,EJB Bean,CDI Bean,EJB自动计时器等,您可以使用功能丰富的批处理应用程序。写起来很容易。
本文还简要介绍了GlassFish 4.0管理控制台和CLI对查询批处理JobRepository
支持。 管理控制台和CLI均提供了有关作业和步骤的有价值的详细信息,可用于检测潜在的瓶颈。
JSR 352支持许多令人兴奋的功能,例如批处理,拆分,流和自定义检查点,这些将在以后的文章中介绍。
作者简介 : Mahesh Kannan是Oracle云应用基金会团队的一名高级软件工程师,并且是Java Batch JSR的专家组成员。 由于他在应用程序服务器,容器和分布式系统方面的丰富经验,他担任过许多为Oracle产品构建创新解决方案的项目的首席架构师和“一般顾问”。
经Oracle公司Oracle技术网许可转载。
封面图片由losmininos提供
翻译自: https://jaxenter.com/java-ee-7-an-overview-of-batch-processing-106275.html
java与java ee