onderzoek de nieuwe batch processing mogelijkheden van JSR 352 voor Java EE 7.
batchverwerking wordt in veel industrieën gebruikt voor taken variërend van payrollverwerking; statement generatie; end-of-day taken zoals renteberekening en ETL (extract, load, and transform) in een datawarehouse; en nog veel meer. Typisch, batchverwerking is bulk-georiënteerd, niet-interactief, en langdurig—en kan data – of berekening-intensief zijn. Batch-taken kunnen op schema worden uitgevoerd of op aanvraag worden gestart. Aangezien batchtaken doorgaans langlopende taken zijn, zijn controle wijzen en opnieuw opstarten ook veelvoorkomende functies in batchtaken.
JSR 352 (Batch Processing for Java Platform), onderdeel van het onlangs geïntroduceerde Java EE 7 platform, definieert het programmeermodel voor batch-toepassingen plus een runtime om batch-taken uit te voeren en te beheren. Dit artikel behandelt een aantal van de belangrijkste concepten, waaronder functie highlights, een overzicht van geselecteerde API ‘ s, de structuur van Taakspecificatie taal, en een voorbeeld batch toepassing. Het artikel beschrijft ook hoe u batch-toepassingen kunt uitvoeren met behulp van GlassFish Server Open Source Edition 4.0.
Batch Processing Architecture
Deze sectie en Figuur 1 beschrijven de basiscomponenten van de batch processing architecture.
figuur 1
- een taak omvat het hele batchproces. Een taak bevat een of meer stappen. Een taak wordt samengesteld met behulp van een Job Specification Language (JSL) die de volgorde specificeert waarin de stappen moeten worden uitgevoerd. In JSR 352 wordt JSL gespecificeerd in een XML-bestand genaamd het job XML-bestand. Kortom, een taak (met JSR 352) is in principe een container voor stappen.
- een step is een domeinobject dat een onafhankelijke, sequentiële fase van de taak inkapselt. Een stap bevat alle benodigde logica en gegevens om de daadwerkelijke verwerking uit te voeren. De batchspecificatie laat opzettelijk de definitie van een stap vaag omdat de inhoud van een stap puur toepassingsspecifiek is en zo complex of eenvoudig kan zijn als de ontwikkelaar wenst. Er zijn twee soorten stappen: chunk en batchlet.
- een stap in chunkstijl bevat precies één
ItemReader
, éénItemProcessor
, en éénItemWriter
. In dit patroon leestItemReader
één item tegelijk,ItemProcessor
verwerkt het item op basis van de business logica (zoals “bereken het saldo van de rekening”), en geeft het aan de batch runtime voor aggregatie. Zodra het” chunk-size”aantal items wordt gelezen en verwerkt, worden ze gegeven aan eenItemWriter
, die de gegevens schrijft (bijvoorbeeld naar een databasetabel of een plat bestand). De transactie wordt dan vastgelegd. - JSR 352 definieert ook een rol-je-eigen soort van een stap die een batchlet wordt genoemd. Een batchlet is vrij om alles te gebruiken om de stap te bereiken, zoals het verzenden van een e-mail.
- een stap in chunkstijl bevat precies één
-
JobOperator
biedt een interface om alle aspecten van taakverwerking te beheren, inclusief operationele commando ‘ s, zoals start, herstart en stop, evenals taakopslagopdrachten, zoals het ophalen van taak-en step-uitvoeringen. Zie paragraaf 10.4 van de JSR 352 specificatie voor meer details overJobOperator
. -
JobRepository
bevat informatie over taken die momenteel worden uitgevoerd en taken die in het verleden zijn uitgevoerd.JobOperator
biedt API ‘ s om toegang te krijgen tot deze repository. EenJobRepository
kan worden geïmplementeerd met behulp van bijvoorbeeld een database of een bestandssysteem.
het ontwikkelen van een eenvoudige Payroll Processing applicatie
Dit artikel toont enkele van de belangrijkste kenmerken van JSR 352 met behulp van een eenvoudige payroll processing applicatie. De aanvraag is bewust heel eenvoudig gehouden om zich te concentreren op de kernbegrippen van JSR 352.
de SimplePayrollJob
batchtaak omvat het lezen van invoergegevens voor loonadministratie uit een CSV-bestand (comma-separated values). Elke regel in het bestand bevat een werknemer-ID en het basissalaris (per maand) voor een werknemer. De batch job berekent vervolgens de belasting die moet worden ingehouden, de bonus, en het netto salaris. De baan moet eindelijk de verwerkte payroll records in een database tabel te schrijven.
we gebruiken een CSV-bestand in dit voorbeeld alleen maar om aan te tonen dat JSR 352 batch-toepassingen toestaat om te lezen en te schrijven vanuit elke willekeurige bron.
Taakspecificatie taal voor de Payroll Processing applicatie
we bespraken dat een step een domeinobject is dat een onafhankelijke, sequentiële fase van de taak inkapselt, en dat een taak in principe een container is voor een of meer stappen.
in JSR 352 specificeert een JSL in principe de volgorde waarin stappen moeten worden uitgevoerd om de taak te volbrengen. De JSL is krachtig genoeg om voorwaardelijke uitvoering van stappen toe te staan, en het staat ook toe dat elke stap zijn eigen eigenschappen heeft, luisteraars, enzovoort.
een batch-toepassing kan zoveel JSLs hebben als het wil, waardoor het zoveel batch-taken kan starten als nodig is. Een applicatie kan bijvoorbeeld twee JSLs hebben, een voor payroll verwerking en een andere voor het genereren van rapporten. Elke JSL moet een unieke naam hebben en moet in de META-INF/batch-jobs
map worden geplaatst. Submappen onder META-INF/batch-jobs
worden genegeerd.
onze JSL voor payroll verwerking wordt geplaatst in een bestand genaamd SimplePayrollJob.xm
l en ziet eruit als lijst 1:
<job xmlns=http://xmlns.jcp.org/xml/ns/javaee version="1.0"> <step> <chunk item-count="2"> <reader ref="simpleItemReader/> <processor ref="simpleItemProcessor/> <writer ref="simpleItemWriter/> </chunk> </step></job>
Listing 1
onze SimplePayrollJob
batch-taak heeft slechts één stap (genaamd”proces”). Het is een chunk-stijl stap en heeft (zoals vereist voor een chunk-stijl stap), een ItemReader
, een ItemProcessor
, en een ItemWriter
. De implementaties voor ItemReader
ItemProcessor
, en ItemWriter
voor deze stap zijn opgegeven met de ref
attribuut in de <reader>
<processor>
, en <writer>
– elementen.
wanneer de taak wordt verzonden (we zullen later zien hoe batch-taken worden verzonden), begint de batch runtime met de eerste stap in de JSL en loopt zijn weg door totdat de volledige taak is voltooid of een van de stappen mislukt. De JSL is krachtig genoeg om zowel voorwaardelijke stappen als parallelle uitvoering van stappen toe te staan, maar we zullen deze details in dit artikel niet behandelen.
Het item-count
attribuut, dat wordt gedefinieerd als 2
In Lijst 1, definieert de grootte van het stuk.
Hier is een overzicht op hoog niveau van hoe chunk-stijl stappen worden uitgevoerd. Zie paragraaf 11.6 (“regelmatige verwerking van brokken”) van de JSR 352-specificatie voor meer details.
- Start een transactie.
- roep de
ItemReader
aan en geef het item gelezen door deItemReader
door aan deItemProcessor
ItemProcessor
verwerkt het item en retourneert het verwerkte item naar de batch runtime. - de batch runtime herhaalt Stap 2
item-count
tijden en houdt een lijst bij van verwerkte items. - de batch runtime roept het
ItemWriter
aan datitem-count
aantal verwerkte items schrijft. - als uitzonderingen worden gegooid van
ItemReader
ItemProcessor
, ofItemWriter
, mislukt de transactie en wordt de stap gemarkeerd als ” mislukt.”Zie paragraaf 5.2.1.2.1 (“uitzonderingen overslaan”) in de JSR 352-specificatie. - als er geen uitzonderingen zijn, verkrijgt de batch runtime checkpoint data van
ItemReader
enItemWriter
(zie paragraaf 2.5 in de JSR 352 specificatie voor meer details). De batch runtime commit de transactie. - stappen 1 tot en met 6 worden herhaald als de
ItemReader
meer gegevens heeft om te lezen.
Dit betekent dat in ons voorbeeld de batch runtime twee records zal lezen en verwerken en de ItemWriter
twee records per transactie zal wegschrijven.
Schrijven ItemReader
ItemProcessor
, en ItemWriter
Schrijven ItemReader
Onze payroll verwerking van de batch JSL definieert één brok stijl stap en geeft aan dat de stap maakt gebruik van een ItemReader
naam simpleItemReader
. Onze applicatie bevat een implementatie van ItemReader
om input CSV data te lezen. Lijst 2 toont een fragment van onze ItemReader
:
@Namedpublic class SimpleItemReader extends AbstractItemReader { @Inject private JobContext jobContext; ...}
Listing 2
merk op dat de klasse is geannoteerd met de @Named
annotatie. Omdat de@Named
annotatie de standaardwaarde gebruikt, is de contexten en Dependency Injection (CDI) naam voor deze beansimpleItemReader
. De JSL specificeert de CDI naam van hetItemReader
in het<reader>
element. Hierdoor kan de batch runtime (via CDI) onze ItemReader
instantiëren wanneer de stap wordt uitgevoerd.
onze ItemReader
injecteert ook een JobContext
JobContext
staat het batch-artefact (ItemReader
, in dit geval) toe om waarden te lezen die zijn doorgegeven tijdens het indienen van taken.
onze payroll SimpleItemReader
overschrijft de open()
methode om de invoer te openen waaruit de payroll-invoergegevens worden gelezen. Zoals we later zullen zien, zal de parameter prevCheckpointInf
o niet null zijn als de taak opnieuw wordt opgestart.
in ons voorbeeld opent de open()
methode, die wordt weergegeven in Lijst 3, het payroll invoerbestand (dat samen met de toepassing is verpakt).
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(" Opened Payroll file for reading from record number: " + recordNumber); }
Listing 3
De readItem()
methode leest in principe één regel met gegevens uit het invoerbestand en bepaalt of de regel twee gehele getallen bevat (een voor werknemers-ID en een voor basissalaris). Als er twee gehele getallen zijn, maakt en retourneert het een nieuwe instantie van PayrollInputRecord
en keert terug naar de batch runtime (die vervolgens wordt doorgegeven aan ItemWriter
).
public Object readItem() throws Exception { Object record = null; if (line != null) { String fields = line.split("+"); PayrollInputRecord payrollInputRecord = new PayrollInputRecord(); payrollInputRecord.setId(Integer.parseInt(fields)); payrollInputRecord.setBaseSalary(Integer.parseInt(fields)); record = payrollInputRecord; //Now that we could successfully read, Increment the record number recordNumber++; } return record;}
Listing 4
de methode checkpointInfo()
wordt aangeroepen door de batch runtime aan het einde van elke succesvolle chunktransactie. Hiermee kan de lezer de laatste succesvolle leespositie controleren.
in ons voorbeeld geeft de checkpointInfo()
de recordNumber
het aantal records aan dat met succes is gelezen, zoals weergegeven in lijst 5.
@Overridepublic Serializable checkpointInfo() throws Exception { return recordNumber;}
Listing 5
schrijven van de ItemProcessor
onze SimpleItemProcessor
volgt een patroon dat lijkt op het patroon voor SimpleItemReader
.
de processItem()
methode ontvangt (uit de batch runtime) de PayrollInputRecord
. Het berekent vervolgens de belasting en het net en geeft een PayrollRecord
terug als output. Merk in lijst 6 op dat het type object dat wordt geretourneerd door een ItemProcessor
heel anders kan zijn dan het type object dat het ontving van ItemReader
.
@Namedpublic 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; } }
Listing 6
schrijven van de ItemWriter
nu moet SimpleItemWriter
voorspelbare regels voor u volgen.
het enige verschil is dat het een EntityManager
injecteert, zodat het de PayrollRecord
instanties (die JPA-entiteiten zijn) in een database kan laten bestaan, zoals weergegeven in Lijst 7.
@Namedpublic 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); } }}
Listing 7
De writeItems()
methode houdt allePayrollRecord
instanties in een databasetabel aan met behulp van JPA. Er zullen ten hoogste item-count
items (de chunk grootte) in de lijst staan.
nu we onze JSL, ItemReader
ItemProcessor
, en ItemWriter
klaar hebben, laten we eens kijken hoe een batch-taak kan worden ingediend.
een Batch-taak starten met een Servlet
merk op dat de aanwezigheid van een taak XML-bestand of andere batch-artefacten (zoals ItemReader
) niet betekent dat een batch-taak automatisch wordt gestart wanneer de toepassing wordt geïmplementeerd. Een batch-taak moet expliciet worden gestart, laten we zeggen, vanaf een servlet of van een Enterprise JavaBeans (EJB) timer of een EJB business methode.
in onze payroll-applicatie gebruiken we een servlet (genaamd PayrollJobSubmitterServlet
) om een batch-taak in te dienen. De servlet toont een HTML-pagina die aan de gebruiker een formulier met twee knoppen presenteert. Wanneer op de eerste knop, genaamd Bereken Payroll, wordt geklikt, roept de servlet de startNewBatchJob
methode aan, weergegeven in lijst 8, die een nieuwe batch-taak start.
private long startNewBatchJob()throws Exception { JobOperator jobOperator = BatchRuntime.getJobOperator(); Properties props = new Properties(); props.setProperty("payrollInputDataFileName", payrollInputDataFileName); return jobOperator.start(JOB_NAME, props);}
Listing 8
de eerste stap is het verkrijgen van een instantie van JobOperator
. Dit kan worden gedaan door het volgende te bellen::
JobOperator jobOperator = BatchRuntime.getJobOperator();
de servlet maakt dan een Properties
object en slaat de naam van het invoerbestand erin op. Tot slot wordt een nieuwe batchtaak gestart door het volgende aan te roepen:
jobOperator.start(jobName, properties)
de jobname
is niets anders dan de JSL XML-bestandsnaam van de taak (minus de .xml
extensie). De parameter properties
dient om alle invoergegevens door te geven aan de taak. HetProperties
object (dat de naam van het payroll-invoerbestand bevat) wordt beschikbaar gemaakt voor andere batchartefacten (zoalsItemReader
ItemProcessor
, enzovoort) via deJobContext
interface.
de batch runtime kent een unieke ID toe, genaamd de execution ID, om elke uitvoering van een taak te identificeren of het een pas aangeleverde taak is of een herstart taak. Veel van de JobOperator
methoden nemen de execution ID als parameter. Met behulp van de execution ID kan een programma de huidige (en vroegere) uitvoeringsstatus en andere statistieken over de taak verkrijgen. De methode JobOperator.start()
geeft het uitvoer-ID terug van de taak die is gestart.
Details over Batch-taken ophalen
wanneer een batch-taak wordt ingediend, maakt de batchruntime een instantie van JobExecution
om deze te volgen. JobExecution
heeft methoden om verschillende details te verkrijgen, zoals de starttijd van de taak, de voltooiingstijd van de taak, de status van taakuitgangsfunctie, enzovoort. Om de JobExecution
voor een uitvoer-ID te verkrijgen, kunt u de methode JobOperator.getJobExecution(executionId)
gebruiken. Listing 9 toont de definitie van 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();}
Listing 9
Verpakking de Toepassing
Nu hebben we onze JSL, ItemReader
ItemProcessor
ItemWriter
, en onze servlet klaar, het is tijd om te verpakken en klaar te implementeren.
u kunt uw batchtoepassing implementeren als een van de ondersteunde Java EE-Archieven (bijvoorbeeld .war
.jar
, of .ear
). U kunt uw batch artefact klassen bundelen samen met andere Java EE klassen (zoals EJB bonen en servlets).
de enige speciale vereiste is dat u uw taak JSLs onder de META-INF/batch-jobs
map voor .jar
bestanden moet plaatsen. Voor .war
archieftypen, plaatst u uw taak JSLs onder de WEB-INF/classes/META-INF/batch-jobs
map.
implementeren en uitvoeren van de Payroll Sample applicatie in GlassFish 4.0
laten we de payroll applicatie die we hebben ontwikkeld implementeren in de GlassFish 4.0 application server. GlassFish 4.0 is de referentie implementatie (RI)voor de Java EE 7.0 specificatie en bevat ook de RI voor JSR 352. U kunt meer informatie vinden over GlassFish 4.0 bij http://glassfish.org en over de Java Batch 1.0 RI bij https://javaee.github.io/.
GlassFish 4.0
U kunt GlassFish 4.0 downloaden van https://glassfish.java.net/download.html en vervolgens installeren. Start GlassFish 4.0 door een opdrachtvenster te openen en het volgende commando uit te voeren:
<GlassFish Install Dir>/bin/asadmin start-domain
omdat de sample payroll applicatie een database gebruikt (om verwerkte gegevens uit te schrijven), hebben we een database nodig voordat we onze applicatie kunnen uitvoeren. U kunt de Apache Derby database starten door het volgende commando uit te voeren:
<GlassFish Install Dir>/bin/asadmin start-database
compileren, verpakken en implementeren van de Payroll applicatie
maak eerst een nieuwe map aan met de naam hello-batch
. Ga dan naar dehello-batch
map:
cd hello-batch
om te compileren en te verpakken, voer je het volgende commando uit, dat hello-batch.war
aanmaakt onder de doelmap:
mvn clean package
om hello-batch.war
, voer het volgende commando uit:
<GlassFish Install Dir>/bin/asadmin deploy target/hello-batch.war
als u de toepassing wilt herschikken, kunt u het volgende commando uitvoeren:
<GlassFish Install Dir>/bin/asadmin deploy -force target/hello-batch.war
het uitvoeren van de Payroll-toepassing
zodra u het hello-batch.war
bestand implementeert, kunt u de toepassing uitvoeren door http://localhost:8080/hello-batch/PayrollJobSubmitterServlet
vanuit een browser te benaderen. Toegang tot deze URL moet het scherm weergegeven in Figuur 2.
Figuur 2
klik op de knop Payroll berekenen en u ziet een nieuwe vermelding in de tabel, zoals weergegeven in Figuur 3.
Figuur 3
klik op de knop Vernieuwen en u zou de Afsluitstatus en Eindtijdkolommen bijgewerkt moeten zien voor de laatste taak (zie Figuur 4). In de kolom Afsluitstatus wordt weergegeven of de taak is mislukt of voltooid. Omdat onze SimplePayrollJob
geen fouten bevat (tenminste nog niet!), de Exit Status wordt weergegeven voltooid.
Figuur 4
Klik nog een paar keer op de knoppen Payroll berekenen en vernieuwen. Merk op dat elke keer dat een taak wordt gestart, een nieuwe uitvoering-ID (en instantie-ID) wordt gegeven aan de taak, zoals weergegeven in Figuur 5.
Figuur 5
herstarten van mislukte taken
tot nu toe hadden we batchtaken gestart met behulp van de jobOperator.start()
methode. Laten we zeggen dat onze payroll input bestand heeft een aantal fouten. Ofwel de ItemReader
of de ItemProcessor
kan ongeldige records detecteren en de huidige stap en de taak falen. De beheerder of de eindgebruiker kan de fout herstellen en kan de batch-taak opnieuw opstarten. Deze aanpak van de lancering van een nieuwe taak die begint vanaf het begin na het herstellen van fouten misschien niet schaal als de hoeveelheid gegevens te verwerken is groot. JobOperator
biedt een andere methode genaamd restart()
om precies dit probleem op te lossen.
snel overzicht van JobInstance
en JobExecution
We zagen eerder dat een taak in wezen een container voor stappen is. Wanneer een taak wordt gestart, moet deze worden gevolgd, zodat de batch runtime een JobInstance
aanmaakt. Een JobInstance
verwijst naar het concept van een logische run. In ons voorbeeld hebben we een PayrollJob
en als de PayrollJob
elke maand wordt uitgevoerd, zal er een Jan-2013 JobInstance
zijn en zal er een andere Feb-2013 JobInstance
zijn, enzovoort.
als de payrollverwerking voor Jan-2013 mislukt, moet deze opnieuw worden gestart (na vermoedelijk de fout te hebben hersteld), maar het is nog steeds de jan-2013 run omdat het nog steeds jan-2013 records verwerkt.
A JobExecution
verwijst naar het concept van een enkele poging om een taak uit te voeren. Elke keer dat een taak wordt gestart of herstart, wordt een nieuwe JobExecution
aangemaakt die behoort tot dezelfde JobInstance
. In ons voorbeeld, als de Jan-2013 JobInstance
herstart wordt, is het nog steeds dezelfde Jan-2013 JobInstance
maar een nieuwe JobExecution
wordt gemaakt die behoort tot dezelfde JobInstance
.
samengevat kan een taak een of meer instanties van JobInstance
hebben en elke JobInstance
kan een of meer JobExecution
instanties hebben. Het gebruik van een nieuwe JobInstance
betekent “begin vanaf het begin” en het gebruik van een bestaande JobInstance
betekent in het algemeen “begin vanaf waar je gestopt was.”
hervatten van mislukte taken
als u zich herinnert, wordt een stap in chunk-stijl uitgevoerd in een transactie waarin item-count
items worden gelezen, verwerkt en geschreven. Nadat de ItemWriter
’s writeItems()
is aangeroepen, roept de batch runtime de checkpointInfo()
methode aan op zowel ItemReader
en ItemWriter
. Dit staat zowel ItemReader
en ItemWriter
toe om een bladwijzer te maken (opslaan) van hun huidige voortgang. De gegevens die als bladwijzer zijn gemarkeerd voor een ItemReader
kunnen alles zijn wat het kan helpen om het lezen te hervatten. Bijvoorbeeld, onze SimpleItemReader
moet het regelnummer opslaan waartoe het tot nu toe succesvol heeft gelezen.
paragraaf 10.8 van de JSR 352 specificatie beschrijft de herstart verwerking in detail.
laten we een moment nemen om in het logbestand te kijken waar onze SimpleItemReader
enkele nuttige berichten uitbrengt van de open()
en checkpoint()
methoden. Elk bericht wordt voorafgegaan door de tekenreeks zodat u snel de berichten kunt identificeren. Het logbestand bevindt zich op
<GlassFish install Dir>/domains/domain1/logs/server.log
.
Listing 10 toont de berichten die zijn voorafgegaan door de tekenreeks :
Opened Payroll File. Will start reading from record number: 0]] checkpointInfo() called. Returning current recordNumber: 2]] checkpointInfo() called. Returning current recordNumber: 4]] checkpointInfo() called. Returning current recordNumber: 6]] checkpointInfo() called. Returning current recordNumber: 8]] checkpointInfo() called. Returning current recordNumber: 9]] close called.]]
Listing 10
Opmerking: U kunt ook het commando tail -f server.log | grep SimpleItemReader
gebruiken.
omdat ons taak XML-bestand (SimplePayrollJob.xml
) een waarde specificeert van 2
voor item-count
als de chunkgrootte, roept de batchruntime checkpointInfo()
op onze ItemReader
elke twee records. De batch runtime slaat deze controlepuntinformatie op in JobRepository
. Dus, als er een fout optreedt tijdens het midden van onze chunk verwerking, de batch applicatie moet in staat zijn om te hervatten vanaf de laatste succesvolle checkpoint.
laten we enkele fouten in ons invoerbestand introduceren en zien hoe we kunnen herstellen van invoerfouten.
Als u kijkt naar de uitvoer van onze servlet, die zich bevindt onder <GlassFish install Dir>/domains/domain1/applications/hello-batch/WEB-INF/classes/payroll-data/payroll-data.csv
, ziet u dat het de locatie weergeeft van het invoerbestand van waaruit CSV-gegevens worden gelezen voor onze payroll-toepassing. Listing 11 toont de inhoud van het bestand:
1, 81002, 82003, 83004, 84005, 85006, 86007, 87008, 88009, 8900
Listing 11
Open uw favoriete editor en voer een fout in. Bijvoorbeeld, laten we zeggen dat we een paar tekens toevoegen aan het salarisveld op het achtste record, zoals weergegeven in Lijst 12:
1, 81002, 82003, 83004, 84005, 85006, 86007, 87008, abc88009, 8900
Listing 12
sla het bestand op en sluit de editor. Ga terug naar uw browser en klik op de knop Payroll berekenen gevolgd door de knop Vernieuwen. U zou zien dat de onlangs ingediende taak mislukt is, zoals weergegeven in Figuur 6. (Kijk naar de kolom Exit Status.)
Figuur 6
u zult ook merken dat er een herstartknop verschijnt naast het uitvoer-ID van de zojuist mislukte taak. Als u op Vernieuwen klikt, zal de taak mislukken (omdat we het probleem nog niet hebben opgelost). Figuur 7 laat zien wat er wordt weergegeven na een paar klikken op de knop Vernieuwen.
Figuur 7
Als u in het GlassFish-serverlog kijkt (onder <GlassFish install Dir>/domains/domain1/logs/server.log
), zult u een uitzondering zien, zoals weergegeven in lijst 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)
listing 13
u moet ook merken dat wanneer u op de knop Herstart klikt, een nieuwe taakuitvoering wordt gemaakt, maar de ID van de taakinstantie blijft hetzelfde. Wanneer u op de knop Vernieuwen klikt, roept onze PayrollJobSubmitter
servlet een methode aan met de naam restartBatchJob()
, die wordt weergegeven in lijst 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);}
lijst 14
de belangrijkste regel in lijst 14 is de aanroep naar JobOperator
’s restart()
methode. Deze methode neemt eenProperties
object, net alsstart()
, maar in plaats van het doorgeven van een taak XML-bestandsnaam, geeft het de uitvoering-ID van de laatst mislukte taak. Met behulp van de execution ID van de meest recent mislukte taak kan de batch runtime het laatste succesvolle controlepunt van de vorige uitvoering ophalen. De opgehaalde checkpointgegevens worden doorgegeven aan de open()
methode van onze SimpleItemReader
(en ItemWriter
) om hen in staat te stellen verder te lezen (en te schrijven) vanaf het laatste succesvolle checkpoint.
terwijl u ervoor zorgt dat uw browser de pagina toont met een herstartknop, bewerk het bestand opnieuw en verwijder de vreemde tekens uit het achtste record. Klik vervolgens op de herstart en ververs knoppen. De laatste uitvoering moet een voltooide status weergeven, zoals weergegeven in Figuur 8.
Figuur 8
Het is tijd om in het logbestand te kijken om te begrijpen wat er zojuist is gebeurd. Nogmaals, op zoek naar berichten die vooraf zijn gegaan met SimpleItemReader
, de lijst 15 toont wat je zou kunnen zien:
Opened Payroll File. Will start reading from record number: 7]] checkpointInfo() called. Returning current recordNumber: 9]] checkpointInfo() called. Returning current recordNumber: 10]] close called.]]
Listing 15
zoals u kunt zien, werd onze SimpleItemReader
‘sopen()
methode aangeroepen met de vorige checkpoint waarde (dat recordnummer 7 was) waardoor onzeSimpleItemReader
om de eerste zes records over te slaan en het lezen van het zevende record te hervatten.
Batch-taken bekijken met behulp van de GlassFish 4.0-beheerconsole
kunt u de lijst van alle batch-taken bekijken in de JobRepository
. Start een browservenster op en ga naar localhost:4848
. Klik vervolgens op server (Admin Server) in het linkerpaneel, zoals weergegeven in Figuur 9.
figuur 9
u kunt op het tabblad Batch klikken, dat alle batch-taken moet weergeven die naar deze GlassFish-server zijn verzonden. Merk op dat de JobRepository
geà mplementeerd wordt met behulp van een database en dat daarom de taakgegevens van de GlassFish 4.0-server opnieuw worden opgestart. Figuur 10 toont alle batch-taken in de JobRepository
.
Figuur 10
u kunt ook op een van de ID ’s klikken die worden vermeld onder Uitvoer-ID’ s. Als u bijvoorbeeld op 293 klikt, ziet u details over die uitvoering:
Figuur 11
Meer details over de uitvoering kunnen worden verkregen door op het tabblad Uitvoeringsstappen bovenaan te klikken.
Figuur 12
Kijk naar de statistieken op deze pagina. Het laat zien hoeveel reads, writes en commits werden uitgevoerd tijdens deze uitvoering.
Batch-taken weergeven met GlassFish 4.0 CLI
U kunt ook de details bekijken over taken die in GlassFish 4 worden uitgevoerd.0 server met behulp van de command-line interface (CLI).
om de lijst Te bekijken van batch jobs, open een commando-venster en voer het volgende commando uit:
asadmin list-batch-jobs -l
Je moet het zien uitvoer gelijkaardig aan Figuur 13:
Figuur 13
om de lijst Te bekijken van batch JobExecution
s, kunt u voer dit commando uit:
asadmin list-batch-job-executions -l
Je moet het zien uitvoer gelijkaardig aan Figuur 14:
Figuur 14
het commando toont de voltooiingsstatus van elke uitvoering en ook de taakparameters die aan elke uitvoering worden doorgegeven.
ten slotte, om details te zien over elke stap in een JobExecution
, kunt u het volgende commando gebruiken:
asadmin list-batch-job-steps -l
u zou uitvoer moeten zien vergelijkbaar met figuur 15:
figuur 15
neem nota van de kolom stepmetrics. Het vertelt hoe vaak ItemReader
en ItemWriter
werden aangeroepen en ook hoeveel commits en rollbacks werden gedaan. Dit zijn zeer waardevolle statistieken.
de CLI-uitvoer moet overeenkomen met de beheerconsole-weergave omdat beide dezelfde JobRepository
opvragen.
u kunt asadmin help <command-name>
gebruiken om meer details over de CLI-opdrachten te krijgen.
conclusie
In dit artikel zagen we hoe eenvoudige batch-toepassingen te schrijven, te verpakken en uit te voeren die stappen in chunkstijl gebruiken. We zagen ook hoe de checkpoint-functie van de batch runtime zorgt voor de eenvoudige herstart van mislukte batch-taken. Toch hebben we nauwelijks de oppervlakte van JSR 352 bekrast. Met de volledige set van Java EE componenten en functies tot uw beschikking, waaronder servlets, EJB bonen, CDI bonen, EJB automatische timers, en ga zo maar door, feature-rijke batch toepassingen kunnen vrij gemakkelijk worden geschreven.
Dit artikel behandelde ook (kort) de GlassFish 4.0-beheerconsole en CLI-ondersteuning voor het opvragen van de batch JobRepository
. Zowel de Admin Console als de CLI bieden waardevolle details over taken en stappen die kunnen worden gebruikt om potentiële knelpunten op te sporen.
JSR 352 ondersteunt veel meer opwindende functies, zoals batchlets, splits, flows en aangepaste checkpoints, die zullen worden behandeld in toekomstige artikelen.
zie ook
JSR 352
over de auteur
Mahesh Kannan is een senior Software engineer bij Oracle ‘ s Cloud Application Foundation team, en hij is de Expert Group Member voor de Java Batch JSR. Door zijn uitgebreide ervaring met applicatieservers, containers en gedistribueerde systemen heeft hij als lead architect en “consultant at large” gewerkt aan vele projecten die innovatieve oplossingen voor Oracle-producten ontwikkelden.
doe mee aan het gesprek
doe mee aan het gesprek met de Java-gemeenschap op Facebook, Twitter en het Oracle Java Blog!