Logica voor opnieuw proberen configureren in Spring Batch

1. Overzicht

Standaard mislukt een Spring-batchtaak vanwege eventuele fouten die tijdens de uitvoering zijn opgetreden. Soms willen we echter de veerkracht van onze applicatie verbeteren om met periodieke storingen om te gaan.

In deze korte tutorial, we zullen onderzoeken hoe we logica voor opnieuw proberen configureren in het Spring Batch-framework.

2. Een voorbeeld van een use-case

Laten we zeggen dat we een batchtaak hebben die een CSV-invoerbestand leest:

gebruikersnaam, gebruikers-ID, transactiedatum, transactiebedrag sammy, 1234, 31/10/2015, 10000 john, 9999, 3/12/2015, 12321

Vervolgens verwerkt het elk record door een REST-eindpunt te raken om het leeftijd en postCode attributen:

openbare klasse RetryItemProcessor implementeert ItemProcessor {@Override openbaar transactieproces (transactietransactie) genereert IOException {log.info ("RetryItemProcessor, probeert te verwerken: {}", transactie); HttpResponse antwoord = fetchMoreUserDetails (transaction.getUserId ()); // ontleed de leeftijd van de gebruiker en postCode van reactie en update transactie ... retourtransactie; } ...}

En tot slot genereert het een geconsolideerde output XML:

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

3. Nieuwe pogingen toevoegen aan ItemProcessor

Nu, wat als de verbinding met het REST-eindpunt een time-out vertoont vanwege een traagheid van het netwerk? Als dat het geval is, mislukt onze batchtaak.

In dergelijke gevallen geven we er de voorkeur aan dat de mislukte artikelverwerking een paar keer opnieuw wordt geprobeerd. En dus, laten we onze batchtaak configureren om maximaal drie nieuwe pogingen uit te voeren in geval van fouten:

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

Hier hebben we een telefoontje naar faultTolerant () voor het inschakelen van de functie voor opnieuw proberen. Bovendien, we gebruiken probeer het opnieuw en retryLimit om de uitzonderingen te definiëren die in aanmerking komen voor een nieuwe poging en het maximale aantal nieuwe pogingen voor een item, respectievelijk.

4. Testen van de nieuwe pogingen

Laten we een testscenario hebben waarin het REST-eindpunt terugkeert leeftijd en postCode was maar een tijdje in de problemen. In dit testscenario krijgen we een ConnectTimeoutException alleen voor de eerste twee API-aanroepen, en de derde aanroep zal slagen:

@Test openbare leegte whenEndpointFailsTwicePasses3rdTime_thenSuccess () gooit uitzondering {FileSystemResource verwachtResult = nieuw FileSystemResource (EXPECTED_OUTPUT); FileSystemResource actualResult = nieuwe FileSystemResource (TEST_OUTPUT); when (httpResponse.getEntity ()) .thenReturn (new StringEntity ("{\" age \ ": 10, \" postCode \ ": \" 430222 \ "}")); // mislukt voor de eerste twee aanroepen en passeert de derde keer wanneer (httpClient.execute (any ())) .thenThrow (nieuwe ConnectTimeoutException ("Timeout count 1")) .thenThrow (nieuwe 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 ("VOLTOOID")); AssertFile.assertFileEquals (verwachtResultaat, actueelResultaat); }

Hier is ons werk met succes afgerond. Bovendien blijkt uit de logboeken dat het eerste record met id = 1234 mislukte tweemaal en slaagde uiteindelijk bij de derde poging:

19: 06: 57.742 [main] INFO osbatch.core.job.SimpleStepHandler - Uitvoeringsstap: [retryStep] 19: 06: 57.758 [main] INFO obbatch.service.RetryItemProcessor - Poging om gebruiker te verwerken met id = 1234 19: 06: 57.758 [main] INFO obbatch.service.RetryItemProcessor - Poging om gebruiker te verwerken met id = 1234 19: 06: 57.758 [main] INFO obbatch.service.RetryItemProcessor - Poging om gebruiker te verwerken met id = 1234 19:06: 57.758 [main] INFO obbatch.service.RetryItemProcessor - Poging om gebruiker te verwerken met id = 9999 19: 06: 57.773 [main] INFO osbatch.core.step.AbstractStep - Stap: [retryStep] uitgevoerd in 31 ms

Evenzo, laten we hebben nog een testcase om te zien wat er gebeurt als alle nieuwe pogingen zijn uitgeput:

@Test public void whenEndpointAlwaysFail_thenJobFails () gooit uitzondering {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 dit geval, er werden drie nieuwe pogingen ondernomen voor het eerste record voordat de taak uiteindelijk mislukte vanwege een ConnectTimeoutException.

5. Nieuwe pogingen configureren met XML

Laten we tot slot eens kijken naar het XML-equivalent van de bovenstaande configuraties:

6. Conclusie

In dit artikel hebben we geleerd hoe we logica voor opnieuw proberen configureren in Spring Batch. We hebben gekeken naar zowel Java- als XML-configuraties.

We hebben ook een unit-test gebruikt om te zien hoe de nieuwe pogingen in de praktijk werkten.

Zoals altijd is de voorbeeldcode voor deze tutorial beschikbaar op GitHub.