복붙노트

[SPRING] 스프링 배치 항목 판독기가 한 번만 실행됩니다.

SPRING

스프링 배치 항목 판독기가 한 번만 실행됩니다.

Spring 배치를 구현하려하지만 이상한 문제에 봉착하면 ItemReader 클래스가 한 번만 실행됩니다.

여기에 세부 사항이 있습니다.

구성 : -

우리는 ItemReader, Commit interval이 1 인 ItemWriter를 가진다.

<batch:job id="csrfTokenBatchJob">
    <batch:step id="step1">
      <tasklet>
        <chunk reader="csrfTokenReader" writer="csrfTokenWriter" commit-interval="1"></chunk>
      </tasklet>
    </batch:step>
  </batch:job>

2. 잽 (Job)은 매분마다 트리거 될 예정입니다.

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
      <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
        <property name="jobDetail" ref="jobDetail" />
        <property name="cronExpression" value="0 0/1 * * * ?" />
      </bean>
    </property>
  </bean>

3. 작업 구성

<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.tavant.oauth.batch.job.CSRFTokenJobLauncher" />
    <property name="jobDataAsMap">
        <map>
            <entry key="jobName" value="csrfTokenCleanUpBatchJob" />
            <entry key="jobLocator" value-ref="jobRegistry" />
            <entry key="jobLauncher" value-ref="jobLauncher" />
        </map>
    </property>
</bean>

처음에는 성공적으로 실행되었지만 나중에 실행되지는 않지만 JobLauncher가 실행 중임을 로그에서 볼 수 있습니다.

@Component("csrfTokenReader")
@Scope(value="step")
public class CSRFTokenReader implements ItemReader<List<CSRFToken>> {

    private static final Logger logger = LoggerFactory.getLogger(CSRFTokenReader.class);

    @Autowired
    private CleanService cleanService;

    @Override
    public List<CSRFToken> read() {
        List<CSRFToken> csrfTokenList = null;
        try{

            int keepUpto = Integer.valueOf(PropertiesContext.getInstance().getProperties().getProperty("token.keep", "1"));

            Calendar calTime = Calendar.getInstance();
            calTime.add(Calendar.HOUR, -keepUpto);
            Date toKeep = calTime.getTime();

            csrfTokenList = cleanService.getCSRFTokenByTime(toKeep);
        }
        catch(Throwable th){
            logger.error("Exception in running job At " + new Date() + th);
        }
        if(CollectionUtils.isEmpty(csrfTokenList)){
            return null;
        }
        return csrfTokenList;
    }
}

편집하다:--

public class CSRFTokenJobLauncher extends QuartzJobBean {
    static final String JOB_NAME = "jobName";
    private JobLocator jobLocator;
    private JobLauncher jobLauncher;
    public void setJobLocator(JobLocator jobLocator) {
        this.jobLocator = jobLocator;
    }
    public void setJobLauncher(JobLauncher jobLauncher) {
        this.jobLauncher = jobLauncher;
    }
    @Override
    protected void executeInternal(JobExecutionContext context) {
        Map<String, Object> jobDataMap = context.getMergedJobDataMap();
        String jobName = (String) jobDataMap.get(JOB_NAME);
        log.info("Quartz trigger firing with Spring Batch jobName="+jobName);
        JobParameters jobParameters = getJobParametersFromJobMap(jobDataMap);
        try {
            jobLauncher.run(jobLocator.getJob(jobName), jobParameters);
        }
        catch (JobExecutionException e) {
            log.error("Could not execute job.", e);
        }
    }
    private JobParameters getJobParametersFromJobMap(Map<String, Object> jobDataMap) {
        JobParametersBuilder builder = new JobParametersBuilder();
        for (Entry<String, Object> entry : jobDataMap.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (value instanceof String && !key.equals(JOB_NAME)) {
                builder.addString(key, (String) value);
            }
            else if (value instanceof Float || value instanceof Double) {
                builder.addDouble(key, ((Number) value).doubleValue());
            }
            else if (value instanceof Integer || value instanceof Long) {
                builder.addLong(key, ((Number)value).longValue());
            }
            else if (value instanceof Date) {
                builder.addDate(key, (Date) value);
            }
        }
        return builder.toJobParameters();
    }
}

해결법

  1. ==============================

    1.시간 낭비 후, 문제는 지금 해결 될 것으로 보인다, 나는 tasklet.Now 배치 판독기에서 allow-start-if-complete = "true"를 구성했다.

    시간 낭비 후, 문제는 지금 해결 될 것으로 보인다, 나는 tasklet.Now 배치 판독기에서 allow-start-if-complete = "true"를 구성했다.

    <batch:job id="csrfTokenBatchJob">
        <batch:step id="step1">
          <batch:tasklet allow-start-if-complete="true">
            <batch:chunk reader="csrfTokenReader" writer="csrfTokenWriter" commit-interval="1"></batch:chunk>
          </batch:tasklet>
        </batch:step>
      </batch:job>
    
  2. ==============================

    2.Spring 배치는 데이터베이스에서 모든 작업 실행을 기록합니다. 그래서 스프링 배치는 모든 작업을 차별화해야합니다. 작업이 이미 같은 날에 실행되었는지 여부를 확인하고 작업 매개 변수가 이전 실행과 다르거 나 완료 설정이 가능하면 시작 허용을 제외하고 다시 시작하지 않습니다.

    Spring 배치는 데이터베이스에서 모든 작업 실행을 기록합니다. 그래서 스프링 배치는 모든 작업을 차별화해야합니다. 작업이 이미 같은 날에 실행되었는지 여부를 확인하고 작업 매개 변수가 이전 실행과 다르거 나 완료 설정이 가능하면 시작 허용을 제외하고 다시 시작하지 않습니다.

    OPTION1 : - 위에서 언급 한 것처럼 우리는 allow-start-if-complete = "true"

    OPTION2 : - 항상 현재 날짜 시간 소인 작업 매개 변수를 전달하십시오. 이렇게 작업 매개 변수 값은 항상 고유합니다.

        JobExecution jobExecution = jobLauncher.run(reportJob, new JobParametersBuilder()
                        .addDate("now", new Date())
    

    OPTION3 : - 매번 고유 한 작업 매개 변수를 전달할 필요가 없도록 RunIdIncrementer와 같은 증분기를 사용하십시오.

        @Bean
        public Job job1(JobBuilderFactory jobs, Step s1) {
            return jobs.get("job1")
                    .incrementer(new RunIdIncrementer())
                    .flow(s1)
                    .end()
                    .build();
        }
    
  3. from https://stackoverflow.com/questions/30165680/spring-batch-item-reader-is-executing-only-once by cc-by-sa and MIT license