欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Spring Batch 配置重試邏輯

 更新時(shí)間:2021年09月08日 08:39:15   作者:夢想畫家  
這篇文章主要介紹了Spring Batch 配置重試邏輯,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Spring Batch在處理過程中遇到錯(cuò)誤job默認(rèn)會(huì)執(zhí)行失敗。為了提高應(yīng)用程序的健壯性,我們需要處理臨時(shí)異常造成失敗。本文我們探討如何配置Spring Batch的重試邏輯。

1. 應(yīng)用示例

批處理應(yīng)用讀取csv文件

sammy, 1234, 31/10/2015, 10000
john, 9999, 3/12/2015, 12321

然后,通過調(diào)用rest接口處理每條記錄,獲取用戶的年齡和郵編屬性,為了正確輸出日期,可以在屬性上增加@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)注解:

@XmlRootElement(name = “transactionRecord”)
@Data
public class Transaction {
private String username;
private int userId;
private int age;
private String postCode;
private LocalDateTime transactionDate;
private double amount;
}

處理類如下

public class RetryItemProcessor implements ItemProcessor<Transaction, Transaction> {
    private static final Logger LOGGER = LoggerFactory.getLogger(RetryItemProcessor.class);
    @Autowired
    private CloseableHttpClient closeableHttpClient;
    @Override
    public Transaction process(Transaction transaction) throws IOException, JSONException {
        LOGGER.info("Attempting to process user with id={}", transaction.getUserId());
        HttpResponse response = fetchMoreUserDetails(transaction.getUserId());
        //parse user's age and postCode from response and update transaction
        String result = EntityUtils.toString(response.getEntity());
        JSONObject userObject = new JSONObject(result);
        transaction.setAge(Integer.parseInt(userObject.getString("age")));
        transaction.setPostCode(userObject.getString("postCode"));
        return transaction;
    }
    private HttpResponse fetchMoreUserDetails(int id) throws IOException {
        final HttpGet request = new HttpGet("http://www.testapi.com:81/user/" + id);
        return closeableHttpClient.execute(request);
    }
}

這里當(dāng)然也可以使用RestTemplate進(jìn)行調(diào)用,調(diào)用服務(wù)僅為了測試,讀者可以搭建測試接口。

最終輸出結(jié)果為

<transactionRecord>
    <transactionRecord>
        <amount>10000.0</amount>
        <transactionDate>2015-10-31 00:00:00</transactionDate>
        <userId>1234</userId>
        <username>sammy</username>
        <age>10</age>
        <postCode>430222</postCode>
    </transactionRecord>
    ...
</transactionRecord>

2. 給處理增加重試功能

如果連接rest接口因?yàn)榫W(wǎng)絡(luò)不穩(wěn)定導(dǎo)致連接超時(shí),那么批處理將失敗。但這種錯(cuò)誤并不是不能恢復(fù),可以通過重試幾次進(jìn)行嘗試。

因此我們配置批處理job在失敗的情況下重試三次

@Configuration
@EnableBatchProcessing
public class SpringBatchRetryConfig {
    private static final String[] tokens = { "username", "userid", "transactiondate", "amount" };
    private static final int TWO_SECONDS = 2000;
    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
    @Value("input/recordRetry.csv")
    private Resource inputCsv;
    @Value("file:xml/retryOutput.xml")
    private Resource outputXml;
    public ItemReader<Transaction> itemReader(Resource inputData) throws ParseException {
        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
        tokenizer.setNames(tokens);
        DefaultLineMapper<Transaction> lineMapper = new DefaultLineMapper<>();
        lineMapper.setLineTokenizer(tokenizer);
        lineMapper.setFieldSetMapper(new RecordFieldSetMapper());
        FlatFileItemReader<Transaction> reader = new FlatFileItemReader<>();
        reader.setResource(inputData);
        reader.setLinesToSkip(1);
        reader.setLineMapper(lineMapper);
        return reader;
    }
    @Bean
    public CloseableHttpClient closeableHttpClient() {
        final RequestConfig config = RequestConfig.custom()
          .setConnectTimeout(TWO_SECONDS)
          .build();
        return HttpClientBuilder.create().setDefaultRequestConfig(config).build();
    }
    @Bean
    public ItemProcessor<Transaction, Transaction> retryItemProcessor() {
        return new RetryItemProcessor();
    }
    @Bean
    public ItemWriter<Transaction> itemWriter(Marshaller marshaller) {
        StaxEventItemWriter<Transaction> itemWriter = new StaxEventItemWriter<>();
        itemWriter.setMarshaller(marshaller);
        itemWriter.setRootTagName("transactionRecord");
        itemWriter.setResource(outputXml);
        return itemWriter;
    }
    @Bean
    public Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setClassesToBeBound(Transaction.class);
        return marshaller;
    }
    @Bean
    public Step retryStep(@Qualifier("retryItemProcessor") ItemProcessor<Transaction, Transaction> processor,
      ItemWriter<Transaction> writer) throws ParseException {
        return stepBuilderFactory.get("retryStep")
          .<Transaction, Transaction>chunk(10)
          .reader(itemReader(inputCsv))
          .processor(processor)
          .writer(writer)
          .faultTolerant()
          .retryLimit(3)
          .retry(ConnectTimeoutException.class)
          .retry(DeadlockLoserDataAccessException.class)
          .build();
    }
    @Bean(name = "retryBatchJob")
    public Job retryJob(@Qualifier("retryStep") Step retryStep) {
        return jobBuilderFactory
          .get("retryBatchJob")
          .start(retryStep)
          .build();
    }

這里調(diào)用faultTolerant()方法啟用重試功能,并設(shè)置重試次數(shù)和對(duì)應(yīng)異常。

3. 測試重試功能

我們測試場景,期望接口在一定時(shí)間內(nèi)返回年齡和郵編。前兩次調(diào)用API拋出異常ConnectTimeoutException

第三次成功調(diào)用

@RunWith(SpringRunner.class)
@SpringBatchTest
@EnableAutoConfiguration
@ContextConfiguration(classes = { SpringBatchRetryConfig.class })
public class SpringBatchRetryIntegrationTest {
    private static final String TEST_OUTPUT = "xml/retryOutput.xml";
    private static final String EXPECTED_OUTPUT = "src/test/resources/output/batchRetry/retryOutput.xml";
    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;
    @MockBean
    private CloseableHttpClient closeableHttpClient;
    @Mock
    private CloseableHttpResponse httpResponse;
    @Test
    public void whenEndpointAlwaysFail_thenJobFails() throws Exception {
        when(closeableHttpClient.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"));
    }
    @Test
    public void whenEndpointFailsTwicePasses3rdTime_thenSuccess() throws Exception {
        FileSystemResource expectedResult = new FileSystemResource(EXPECTED_OUTPUT);
        FileSystemResource actualResult = new FileSystemResource(TEST_OUTPUT);
        //前兩次調(diào)用失敗,第三次繼續(xù)執(zhí)行
        when(httpResponse.getEntity())
          .thenReturn(new StringEntity("{ \"age\":10, \"postCode\":\"430222\" }"));
        when(closeableHttpClient.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);
    }
    private JobParameters defaultJobParameters() {
        JobParametersBuilder paramsBuilder = new JobParametersBuilder();
        paramsBuilder.addString("jobID", String.valueOf(System.currentTimeMillis()));
        return paramsBuilder.toJobParameters();
    }
}

job成功執(zhí)行

從日志可以看到兩次失敗,最終調(diào)用成功。

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

同時(shí)也定義了另一個(gè)測試,重試多次并失敗,拋出異常 ConnectTimeoutException。

4. 總結(jié)

本文我們學(xué)習(xí)如何配置Spring Batch的重試邏輯。通過示例學(xué)習(xí)配置并機(jī)型測試,僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java String中移除空白字符的多種方式匯總

    Java String中移除空白字符的多種方式匯總

    這篇文章主要給大家介紹了關(guān)于Java String中移除空白字符的多種方式,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • springboot整合Shiro

    springboot整合Shiro

    這篇文章主要介紹了SpringBoot整合Shiro一些方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • 圖解Java經(jīng)典算法快速排序的原理與實(shí)現(xiàn)

    圖解Java經(jīng)典算法快速排序的原理與實(shí)現(xiàn)

    快速排序是基于二分的思想,對(duì)冒泡排序的一種改進(jìn)。主要思想是確立一個(gè)基數(shù),將小于基數(shù)的數(shù)放到基數(shù)左邊,大于基數(shù)的數(shù)字放到基數(shù)的右邊,然后在對(duì)這兩部分進(jìn)一步排序,從而實(shí)現(xiàn)對(duì)數(shù)組的排序
    2022-09-09
  • java報(bào)錯(cuò):找不到或無法加載主類的解決方法簡單粗暴

    java報(bào)錯(cuò):找不到或無法加載主類的解決方法簡單粗暴

    本文主要介紹了java報(bào)錯(cuò):找不到或無法加載主類的解決方法簡單粗暴,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 淺談Java 對(duì)于繼承的初級(jí)理解

    淺談Java 對(duì)于繼承的初級(jí)理解

    下面小編就為大家?guī)硪黄獪\談Java 對(duì)于繼承的初級(jí)理解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-06-06
  • java使用FileVisitor遍歷文件和目錄

    java使用FileVisitor遍歷文件和目錄

    這篇文章主要為大家詳細(xì)介紹了java使用FileVisitor遍歷文件和目錄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(33)

    Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(33)

    下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你
    2021-07-07
  • Java中的浮點(diǎn)數(shù)分析

    Java中的浮點(diǎn)數(shù)分析

    Java中的浮點(diǎn)數(shù)分析...
    2006-12-12
  • SpringBoot2.x入門教程之引入jdbc模塊與JdbcTemplate簡單使用方法

    SpringBoot2.x入門教程之引入jdbc模塊與JdbcTemplate簡單使用方法

    這篇文章主要介紹了SpringBoot2.x入門教程之引入jdbc模塊與JdbcTemplate簡單使用方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Springboot中@ConfigurationProperties輕松管理應(yīng)用程序的配置信息詳解

    Springboot中@ConfigurationProperties輕松管理應(yīng)用程序的配置信息詳解

    通過@ConfigurationProperties注解,可以將外部配置文件中的屬性值注入到JavaBean中,簡化了配置屬性的讀取和管理,這使得SpringBoot應(yīng)用程序中配置文件的屬性值可以映射到POJO類中,實(shí)現(xiàn)類型安全的屬性訪問,此方法避免了手動(dòng)讀取配置文件屬性的需要
    2024-10-10

最新評(píng)論