Konfigurieren der Wiederholungslogik in Spring Batch

1. Übersicht

Standardmäßig schlägt ein Spring-Stapeljob für alle Fehler fehl, die während seiner Ausführung aufgetreten sind. Manchmal möchten wir jedoch möglicherweise die Ausfallsicherheit unserer Anwendung verbessern, um zeitweise auftretende Fehler zu beheben.

In diesem kurzen Tutorial erfahren Sie, wie Sie die Wiederholungslogik im Spring Batch-Framework konfigurieren .

2. Ein Anwendungsbeispiel

Angenommen, wir haben einen Stapeljob, der eine CSV-Eingabedatei liest:

username, userid, transaction_date, transaction_amount sammy, 1234, 31/10/2015, 10000 john, 9999, 3/12/2015, 12321

Anschließend wird jeder Datensatz verarbeitet, indem ein REST-Endpunkt erreicht wird, um das Alter und die Postcode- Attribute des Benutzers abzurufen :

public class RetryItemProcessor implements ItemProcessor { @Override public Transaction process(Transaction transaction) throws IOException { log.info("RetryItemProcessor, attempting to process: {}", transaction); HttpResponse response = fetchMoreUserDetails(transaction.getUserId()); //parse user's age and postCode from response and update transaction ... return transaction; } ... }

Und schließlich wird ein konsolidiertes Ausgabe- XML generiert :

  10000.0 2015-10-31 00:00:00 1234 sammy 10 430222  ... 

3. Hinzufügen von Wiederholungen zu ItemProcessor

Was ist nun, wenn die Verbindung zum REST-Endpunkt aufgrund einer Netzwerkverlangsamung unterbrochen wird? In diesem Fall schlägt unser Stapeljob fehl.

In solchen Fällen möchten wir, dass die fehlgeschlagene Artikelverarbeitung einige Male wiederholt wird. Konfigurieren wir unseren Batch-Job so, dass bei Fehlern bis zu drei Wiederholungsversuche durchgeführt werden :

@Bean public Step retryStep( ItemProcessor processor, ItemWriter writer) throws ParseException { return stepBuilderFactory .get("retryStep") .chunk(10) .reader(itemReader(inputCsv)) .processor(processor) .writer(writer) .faultTolerant() .retryLimit(3) .retry(ConnectTimeoutException.class) .retry(DeadlockLoserDataAccessException.class) .build(); }

Hier haben wir einen Aufruf von errorTolerant (), um die Wiederholungsfunktion zu aktivieren. Darüber hinaus verwenden wir Wiederholungs und RetryLimit die Ausnahmen zu definieren , die für einen erneuten Versuch und die maximale Wiederholungsanzahl qualifizieren für ein Element sind.

4. Testen der Wiederholungen

Lassen Sie uns ein Testszenario haben, in dem der REST-Endpunkt, der das Alter und den Postcode zurückgibt, nur für eine Weile ausgefallen ist. In diesem Testszenario erhalten wir nur für die ersten beiden API-Aufrufe eine ConnectTimeoutException , und der dritte Aufruf ist erfolgreich:

@Test public void whenEndpointFailsTwicePasses3rdTime_thenSuccess() throws Exception { FileSystemResource expectedResult = new FileSystemResource(EXPECTED_OUTPUT); FileSystemResource actualResult = new FileSystemResource(TEST_OUTPUT); when(httpResponse.getEntity()) .thenReturn(new StringEntity("{ \"age\":10, \"postCode\":\"430222\" }")); //fails for first two calls and passes third time onwards when(httpClient.execute(any())) .thenThrow(new ConnectTimeoutException("Timeout count 1")) .thenThrow(new ConnectTimeoutException("Timeout count 2")) .thenReturn(httpResponse); JobExecution jobExecution = jobLauncherTestUtils .launchJob(defaultJobParameters()); JobInstance actualJobInstance = jobExecution.getJobInstance(); ExitStatus actualJobExitStatus = jobExecution.getExitStatus(); assertThat(actualJobInstance.getJobName(), is("retryBatchJob")); assertThat(actualJobExitStatus.getExitCode(), is("COMPLETED")); AssertFile.assertFileEquals(expectedResult, actualResult); }

Hier wurde unser Job erfolgreich abgeschlossen. Aus den Protokollen geht außerdem hervor, dass der erste Datensatz mit der ID = 1234 zweimal fehlgeschlagen ist und schließlich beim dritten erneuten Versuch erfolgreich war :

19:06:57.742 [main] INFO o.s.batch.core.job.SimpleStepHandler - Executing step: [retryStep] 19:06:57.758 [main] INFO o.b.batch.service.RetryItemProcessor - Attempting to process user with id=1234 19:06:57.758 [main] INFO o.b.batch.service.RetryItemProcessor - Attempting to process user with id=1234 19:06:57.758 [main] INFO o.b.batch.service.RetryItemProcessor - Attempting to process user with id=1234 19:06:57.758 [main] INFO o.b.batch.service.RetryItemProcessor - Attempting to process user with id=9999 19:06:57.773 [main] INFO o.s.batch.core.step.AbstractStep - Step: [retryStep] executed in 31ms

Lassen Sie uns in ähnlicher Weise einen weiteren Testfall durchführen, um zu sehen, was passiert, wenn alle Wiederholungsversuche erschöpft sind :

@Test public void whenEndpointAlwaysFail_thenJobFails() throws Exception { when(httpClient.execute(any())) .thenThrow(new ConnectTimeoutException("Endpoint is down")); JobExecution jobExecution = jobLauncherTestUtils .launchJob(defaultJobParameters()); JobInstance actualJobInstance = jobExecution.getJobInstance(); ExitStatus actualJobExitStatus = jobExecution.getExitStatus(); assertThat(actualJobInstance.getJobName(), is("retryBatchJob")); assertThat(actualJobExitStatus.getExitCode(), is("FAILED")); assertThat(actualJobExitStatus.getExitDescription(), containsString("org.apache.http.conn.ConnectTimeoutException")); }

In diesem Fall wurden drei Wiederholungsversuche für den ersten Datensatz versucht, bevor der Job aufgrund einer ConnectTimeoutException endgültig fehlschlug .

5. Konfigurieren von Wiederholungen mit XML

Schauen wir uns zum Schluss das XML-Äquivalent der obigen Konfigurationen an:

6. Fazit

In diesem Artikel haben wir gelernt, wie Sie die Wiederholungslogik in Spring Batch konfigurieren. Wir haben uns sowohl Java- als auch XML-Konfigurationen angesehen.

Wir haben auch einen Unit-Test verwendet, um zu sehen, wie die Wiederholungsversuche in der Praxis funktionierten.

Wie immer ist der Beispielcode für dieses Tutorial auf GitHub verfügbar.