Quartz, Batch

BB·2023년 1월 6일
0
post-thumbnail

주기적인 작업을 처리하기 위한 Spring 기능들을 찾아보면서.


Quartz

매 시간 또는 매월 마지막 금요일 실행할 무언가를 정의

- @Scheduled 사용한 스케줄링

@SpringBootApplication
@EnableScheduling
public class DemoApplication {..}

@Component
public class PeriodicTask {

	// 5초마다 실행, 1초에 2번 실행
    //@Scheduled(cron = "0/5 * * * * ?")
    @Scheduled(fixedDelay = 500)
    public void everyFiveSeconds(){...}
}

- Directly use Quartz

// @EnableScheduling 사용 x, Quartz 의존성을 추가해야한다.
/**
 * @interface Job
 * 우리가 실행 할 로직을 포함하는 클래스를 구현한다
 * execute @Override
 */
public class SimpleJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {...}
}


@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) throws SchedulerException {
		SpringApplication.run(DemoApplication.class, args);
		OnStartup();
	}


	private static void OnStartup() throws SchedulerException {

		/**
		 * jobDetail
		 * job 인스턴스와 관련 데이터를 정의, 이름과 그룹으로 구성괸 JObKey로 식별됨
		 */
		JobDetail job = JobBuilder.newJob(SimpleJob.class)
				.usingJobData("param", "value")
				.build();

		/**
		 * @param trigger
         * 작업이 실행되는 일정을 정의
         * 트리거는 하나의 작업에만 연결
		 * 1초 위에 trigger가 발생하는 trigger을 정의
		 */
		Date afterFiveSeconds = Date.from(LocalDateTime.now().plusSeconds(1)
				.atZone(ZoneId.systemDefault()).toInstant());
		Trigger trigger = TriggerBuilder.newTrigger()
				.startAt(afterFiveSeconds)
				.build();

		SchedulerFactory schedulerFactory = new StdSchedulerFactory();
		Scheduler scheduler = schedulerFactory.getScheduler();

		/**
		 * schedular.start()
		 * Scheduler 가 대기 모드에서 초기화
		 * scjeduler.scheduleJob()
		 * job 실행을 예약
		 */
		scheduler.start();
		scheduler.scheduleJob(job, trigger);
	}
}

- JobStore

Quartz는 JobDetail, Trigger, 기타정보를 JobStore에 저장한다. 기본 in-memory 이므로 즉, 예약된 작업이 있고 실행되기 전에 응용 프로그램을 종료(예: 다시 시작하거나 충돌할 때)하면 다시는 실행되지 않는다. JDBC-JobStore에 저장하기 위해 다음 설정을 사용

다음과 같은 테이블 생성

# application.yaml
spring:
  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always

이를 위해서는 @Bean, @EnableScheduling 설정을 해야한다.

@SpringBootApplication
@EnableScheduling
public class DemoApplication {
	public static void main(String[] args) throws SchedulerException {...}

	/**
	 * 기존 코드
	 * SchedulerFactory schedulerFactory = new StdSchedulerFactory();
	 * Scheduler scheduler = schedulerFactory.getScheduler();
	 */
	@Bean
	public Scheduler scheduler(SchedulerFactoryBean factory) throws SchedulerException {
		Scheduler scheduler = factory.getScheduler();
		scheduler.start();
		return scheduler;
	}

	@Bean
	public CommandLineRunner run(Scheduler scheduler) {
		return (String[] args) -> {
			JobDetail job = JobBuilder.newJob(SimpleJob.class)
					.usingJobData("param", "value") // add a parameter
					.build();

			Date afterFiveSeconds = Date.from(LocalDateTime.now().plusSeconds(5)
					.atZone(ZoneId.systemDefault()).toInstant());
			Trigger trigger = TriggerBuilder.newTrigger()
					.startAt(afterFiveSeconds)
					.build();

			scheduler.scheduleJob(job, trigger);
		};
	}

- Thread pool configuration

@Scheduled, implements Job 을 통해 생성된 Job은 서로 다른 스레드 풀에서 시작된다.

@Scheduled 는 하나의 스레드만 포함

  • @Scheduled 설정
@Configuration
public class SchedulingConfiguration implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

        // @Scheduled 만 변경
        threadPoolTaskScheduler.setPoolSize(10);
        threadPoolTaskScheduler.setThreadNamePrefix("modified-my-scheduled-task-pool");
        threadPoolTaskScheduler.initialize();

        taskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
    }
}
  • implement Job 설정
    application 파일 설정 또는 SchedulerFactoryBeanCustomizer 을 통해 세팅을 변경
# application.yaml
spring:
  quartz:
    properties:
      org.quartz.threadPool.threadNamePrefix: Modified-My-quartz-Worker
      org.quartz.threadPool.threadCount: 25

설정 후

- Multiple schedulers

매개변수가 다른 스케줄러를 생성하는 경우 여러개의 SchedulerFactoryBreans를 정의해야 한다.

@SpringBootApplication
@EnableScheduling
public class DemoApplication {

	public static void main(String[] args) throws SchedulerException {
		SpringApplication.run(DemoApplication.class, args);

	}


	/**
	 * @param dataSource : JDBCJobStore 를 사용할 때
	 */
	@Bean(name = "CustomSchedulerFactory1")
	public SchedulerFactoryBean customSchedulerFactoryBean1(DataSource dataSource) {
		SchedulerFactoryBean factory = new SchedulerFactoryBean();
		
		// Properties 설정
		Properties properties = new Properties();
		properties.setProperty("org.quartz.threadPool.threadNamePrefix", "my-custom-scheduler1-");
		factory.setQuartzProperties(properties);

		factory.setDataSource(dataSource);
		return factory;
	}

	@Bean(name = "CustomSchedulerFactory2")
	public SchedulerFactoryBean customSchedulerFactoryBean2(DataSource dataSource) {
		SchedulerFactoryBean factory = new SchedulerFactoryBean();

		Properties properties = new Properties();
		properties.setProperty("org.quartz.threadPool.threadNamePrefix", "my-custom-scheduler2-");
		factory.setQuartzProperties(properties);

		factory.setDataSource(dataSource);
		return factory;
	}

	@Bean("CustomScheduler1")
	public Scheduler customScheduler1(@Qualifier("CustomSchedulerFactory1") SchedulerFactoryBean factory) throws SchedulerException {
		Scheduler scheduler = factory.getScheduler();
		scheduler.start();
		return scheduler;
	}

	@Bean("CustomScheduler2")
	public Scheduler customScheduler2(@Qualifier("CustomSchedulerFactory2") SchedulerFactoryBean factory) throws SchedulerException {
		Scheduler scheduler = factory.getScheduler();
		scheduler.start();
		return scheduler;
	}

	@Bean
	public CommandLineRunner run(@Qualifier("CustomScheduler1") Scheduler customScheduler1,
								 @Qualifier("CustomScheduler2") Scheduler customScheduler2) {
		return (String[] args) -> {
			Date afterFiveSeconds = Date.from(LocalDateTime.now().plusSeconds(5)
					.atZone(ZoneId.systemDefault()).toInstant());

			/**
			 * SchedulerFactoryBean -> Scheduler -> CommandLineRunner
			 *
			 * JobDetail(Value1) -> Trigger
			 * JobDetail(Value2) -> Trigger
			 */
			JobDetail jobDetail1 = JobBuilder.newJob(SimpleJob.class)
					.usingJobData("param", "value1")
					.build();
			Trigger trigger1 = TriggerBuilder.newTrigger()
					.startAt(afterFiveSeconds)
					.build();
			customScheduler1.scheduleJob(jobDetail1, trigger1);


			JobDetail jobDetail2 = JobBuilder.newJob(SimpleJob.class)
					.usingJobData("param", "value2").build();
			Trigger trigger2 = TriggerBuilder.newTrigger()
					.startAt(afterFiveSeconds).build();
			customScheduler2.scheduleJob(jobDetail2, trigger2);
		};
	}
}

Batch

실행할 무언가를 정의


ref

Quartz
https://hackernoon.com/how-to-schedule-jobs-with-quartz-in-spring-boot
https://medium.com/@manvendrapsingh/introduction-to-quartz-scheduling-af9b01708c24

profile
공부

0개의 댓글