Konfigurácia logiky opakovania v Spring Batch
1. Prehľad
V predvolenom nastavení jarná dávková úloha zlyhá pre všetky chyby vzniknuté počas jej vykonávania. Niekedy však možno budeme chcieť vylepšiť odolnosť našej aplikácie, aby sme sa vyrovnali s občasnými zlyhaniami.
V tomto rýchlom návode preskúmame, ako nakonfigurovať logiku opakovania v rámci Spring Batch.
2. Príklad použitia
Povedzme, že máme dávkovú úlohu, ktorá číta vstupný súbor CSV:
používateľské meno, identifikátor používateľa, dátum transakcie, suma transakcie sammy, 1234, 31/10/2015, 10 000 john, 9999, 3. 12. 2015, 12321
Potom spracuje každý záznam klepnutím na koncový bod REST, aby sa načítal údaj používateľa Vek a postCode atribúty:
verejná trieda RetryItemProcessor implementuje ItemProcessor {@Override verejný transakčný proces (transakčná transakcia) vyvolá IOException {log.info ("RetryItemProcessor, pokus o spracovanie: {}", transakcia); HttpResponse response = fetchMoreUserDetails (transaction.getUserId ()); // analyzovať vek používateľa a postCode z reakcie a aktualizovať transakciu ... návratová transakcia; } ...}
A nakoniec generuje konsolidovaný výstup XML:
10000.0 2015-10-31 00:00:00 1234 sammy 10 430222 ...
3. Pridanie pokusov do ItemProcessor
Čo teraz, ak pripojenie k koncovému bodu REST vyprší z dôvodu nejakej pomalosti siete? Ak je to tak, naša dávková práca zlyhá.
V takýchto prípadoch by sme uprednostnili opakované pokusy o zlyhanie spracovania položky. A tak, nakonfigurujme našu dávkovú úlohu tak, aby v prípade zlyhania vykonala až tri pokusy:
@Bean public Step retryStep (procesor ItemProcessor, zapisovač ItemWriter) hodí ParseException {return stepBuilderFactory .get ("retryStep") .chunk (10) .reader (itemReader (inputCsv)) .processor (procesor) .writer (spisovateľ). DefaultTolerant ( ) .retryLimit (3) .retry (ConnectTimeoutException.class) .retry (DeadlockLoserDataAccessException.class) .build (); }
Máme tu hovor odolné proti chybám() na povolenie funkcie opakovania. Navyše, používame skúsiť znova a retryLimit definovať výnimky, ktoré sa kvalifikujú na opakovanie, a maximálny počet opakovaní za položku, resp.
4. Testovanie pokusov
Poďme mať testovací scenár, kde sa vráti koncový bod REST Vek a postCode bol dole len na chvíľu. V tomto testovacom scenári dostaneme a ConnectTimeoutException iba pre prvé dve volania API a tretie volanie bude úspešné:
@Test public void whenEndpointFailsTwicePasses3rdTime_thenSuccess () vyvolá výnimku {FileSystemResource expectResult = nový FileSystemResource (EXPECTED_OUTPUT); FileSystemResource actualResult = nový FileSystemResource (TEST_OUTPUT); when (httpResponse.getEntity ()) .thenReturn (new StringEntity ("{\" age \ ": 10, \" postCode \ ": \" 430222 \ "}")); // zlyhá pri prvých dvoch hovoroch a prejde tretíkrát ďalej, keď (httpClient.execute (any ())) .thenThrow (nový ConnectTimeoutException ("časový limit počet 1")) .thThThrow (nový ConnectTimeoutException ("časový limit počet 2")). thenReturn (httpResponse); JobExecution jobExecution = jobLauncherTestUtils .launchJob (defaultJobParameters ()); JobInstance actualJobInstance = jobExecution.getJobInstance (); ExitStatus actualJobExitStatus = jobExecution.getExitStatus (); assertThat (actualJobInstance.getJobName (), je ("retryBatchJob")); assertThat (actualJobExitStatus.getExitCode (), je ("DOKONČENÉ")); AssertFile.assertFileEquals (expectResult, actualResult); }
Tu sa naša práca úspešne dokončila. Z protokolov je navyše zrejmé, že prvý záznam s id = 1234 dvakrát neuspel a nakoniec uspel pri treťom pokuse:
19: 06: 57,742 [main] INFO osbatch.core.job.SimpleStepHandler - krok vykonania: [retryStep] 19: 06: 57.758 [main] INFO obbatch.service.RetryItemProcessor - pokus o spracovanie používateľa s id = 1234 19: 06: 57.758 [main] INFO obbatch.service.RetryItemProcessor - Pokus o spracovanie používateľa s id = 1234 19: 06: 57.758 [main] INFO obbatch.service.RetryItemProcessor - Pokus o spracovanie používateľa s id = 1234 19:06: 57.758 [main] INFO obbatch.service.RetryItemProcessor - Pokus o spracovanie používateľa s id = 9999 19: 06: 57.773 [main] INFO osbatch.core.step.AbstractStep - Krok: [retryStep] vykonaný za 31 ms
Podobne si dajme ďalší testovací prípad, ktorý sleduje, čo sa stane, keď sa vyčerpajú všetky pokusy:
@Test public void whenEndpointAlwaysFail_thenJobFails () vyvolá výnimku {when (httpClient.execute (any ())) .thThTh (new ConnectTimeoutException ("Endpoint is down")); JobExecution jobExecution = jobLauncherTestUtils .launchJob (defaultJobParameters ()); JobInstance actualJobInstance = jobExecution.getJobInstance (); ExitStatus actualJobExitStatus = jobExecution.getExitStatus (); assertThat (actualJobInstance.getJobName (), je ("retryBatchJob")); assertThat (actualJobExitStatus.getExitCode (), je ("FAILED")); assertThat (actualJobExitStatus.getExitDescription (), containsString ("org.apache.http.conn.ConnectTimeoutException")); }
V tomto prípade, o prvý záznam sa pokúsili tri pokusy predtým, ako úloha nakoniec zlyhala kvôli a ConnectTimeoutException.
5. Konfigurácia pokusov pomocou XML
Na záver sa pozrime na ekvivalent XML vyššie uvedených konfigurácií:
6. Záver
V tomto článku sme sa naučili, ako nakonfigurovať logiku opakovania v Spring Batch. Pozreli sme sa na konfiguráciu Java aj XML.
Použili sme tiež test jednotky, aby sme zistili, ako pokusy fungovali v praxi.
Vzorový kód tohto tutoriálu je ako vždy k dispozícii na GitHub.