๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
BackEnd๐ŸŒฑ/Spring

@Scheduled ์‚ฌ์šฉํ•  ๋•Œ ์Šค๋ ˆ๋“œ ์„ค์ •

by dkswnkk 2023. 11. 9.

๊ฐœ์š”

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” @Scheduled ์–ด๋…ธํ…Œ์ด์…˜์€ ๋ฉ”์„œ๋“œ์— ์Šค์ผ€์ค„๋ง ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ @Scheduled ์–ด๋…ธํ…Œ์ด์…˜๋งŒ์„ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํ”„๋ง์€ ๋‹จ์ผ ์Šค๋ ˆ๋“œ์—์„œ ์Šค์ผ€์ค„๋ง ์ž‘์—…๋“ค์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ, ์ด๋Š” ํ•˜๋‚˜์˜ ์Šค์ผ€์ค„๋ง ์ž‘์—…์ด ์™„๋ฃŒ๋˜์–ด์•ผ๋งŒ ๋‹ค์Œ ์Šค์ผ€์ค„๋ง ์ž‘์—…์ด ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋ถ€๊ฐ€์ ์ธ ์„ค์ • ์—†์ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด, ์ผ๋ถ€ ์ž‘์—…์ด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์‹œ๊ฐ„์— ๋™์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ ์ƒํ™ฉ์„ ํ•œ๋ฒˆ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@Configuration
@EnableScheduling
public class ScheduledTasks {
    private final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
    @Scheduled(fixedRate = 1000) // 1์ดˆ๋งˆ๋‹ค taskA๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    public void taskA() throws InterruptedException {
        Thread.sleep(10000); // 10์ดˆ ๋™์•ˆ ์ผ์‹œ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค.
        log.info("taskA - {} - {}", LocalDateTime.now(), Thread.currentThread().getName());
    }
    @Scheduled(fixedRate = 1000) // 1์ดˆ๋งˆ๋‹ค taskB๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    public void taskB() {
        // taskB์˜ ๋กœ์ง์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
        log.info("taskB - {} - {}", LocalDateTime.now(), Thread.currentThread().getName());
    }

์‹คํ–‰ ๊ฒฐ๊ณผ

์œ„์˜ ์˜ˆ์‹œ์—์„œ taskA์™€ taskB๋Š” ๊ฐ๊ฐ 1์ดˆ๋งˆ๋‹ค ์‹คํ–‰๋˜๋„๋ก ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์˜ˆ์ƒ๊ณผ ๋‹ฌ๋ฆฌ ์‹ค์ œ๋กœ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด ๋ณด๋ฉด, taskB๊ฐ€ 10์ดˆ์— ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” @Scheduled ์–ด๋…ธํ…Œ์ด์…˜์˜ ๊ธฐ๋ณธ ์„ค์ •์ด ๋‹จ์ผ ์Šค๋ ˆ๋“œ์—์„œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด์— ๋”ฐ๋ผ, taskA์™€ taskB๋Š” ๋™์ผํ•œ ์Šค๋ ˆ๋“œ์—์„œ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฉฐ, taskA๋Š” 10์ดˆ ๋™์•ˆ ์ผ์‹œ ์ค‘๋‹จ๋˜๋ฏ€๋กœ, taskB๋Š” taskA์˜ ์ˆ˜ํ–‰์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ๋กœ๊ทธ์—์„œ 'scheduling-1'์ด๋ผ๋Š” ๋™์ผํ•œ ์Šค๋ ˆ๋“œ์—์„œ ๋‘ ์ž‘์—…์ด ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ถ€๊ฐ€์ ์ธ ์„ค์ •์ด ํ•„์š”ํ•œ๋ฐ, ํฌ๊ฒŒ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

  1. Async ์ฒ˜๋ฆฌ
  2. SchedulingConfigurer ๊ตฌํ˜„
  3. TaskScheduler ๋นˆ ๋“ฑ๋ก

 
 

1. Async ์ฒ˜๋ฆฌ

์Šคํ”„๋ง์˜ @Async ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ”์„œ๋“œ๊ฐ€ ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ ์ž‘์—…๋“ค์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

@Configuration
@EnableAsync
@EnableScheduling
public class ScheduledTasks {
    private final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
    @Async
    @Scheduled(fixedRate = 1000)
    public void taskA() throws InterruptedException {
        Thread.sleep(10000);
        log.info("taskA - {} - {}", LocalDateTime.now(), Thread.currentThread().getName());
    }
    @Async
    @Scheduled(fixedRate = 1000)
    public void taskB() {
        log.info("taskB - {} - {}", LocalDateTime.now(), Thread.currentThread().getName());
    }
}

์‹คํ–‰ ๊ฒฐ๊ณผ

์œ„์˜ ์ฝ”๋“œ์—์„œ, taskA์™€ taskB๋Š” ๊ฐ๊ฐ 1์ดˆ๋งˆ๋‹ค ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋น„๋™๊ธฐ๋กœ ์‹คํ–‰๋˜๋„๋ก ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋ฉด taskA์˜ ์‹คํ–‰์ด taskB์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์‹คํ–‰ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด ๋ณด๋ฉด, ์‹ค์ œ๋กœ taskA์™€ taskB๊ฐ€ ๊ฐ๊ฐ 1์ดˆ๋งˆ๋‹ค ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” @Async ์–ด๋…ธํ…Œ์ด์…˜์˜ ๊ธฐ๋ณธ ์„ค์ •์ด SimpleAsyncTaskExecutor๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํŠนํžˆ taskA์˜ ๊ฒฝ์šฐ ์ด์ „ ์„ค์ •์—์„œ๋Š” 10์ดˆ ๋™์•ˆ ์ผ์‹œ ์ค‘๋‹จ๋˜์—ˆ์ง€๋งŒ, ์ด๋ฒˆ ์„ค์ •์—์„œ๋Š” 1์ดˆ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋น„๋™๊ธฐ๋กœ ์ˆ˜ํ–‰ํ•˜๋ฉด ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ, ํ•œ ์ž‘์—…์˜ ์ง€์—ฐ์ด ๋‹ค๋ฅธ ์ž‘์—…์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
 
 

2. SchedulingConfigurer ๊ตฌํ˜„

SchedulingConfigurer ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด, ์Šค์ผ€์ค„๋Ÿฌ์˜ ๋™์ž‘์„ ๋”์šฑ ์„ธ๋ถ€์ ์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์Šค๋ ˆ๋“œ ํ’€์˜ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•˜๊ฑฐ๋‚˜ ์Šค๋ ˆ๋“œ์˜ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(10);
        threadPoolTaskScheduler.setThreadNamePrefix("my-scheduler-");
        threadPoolTaskScheduler.initialize();
        taskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
    }
}

์œ„ ์˜ˆ์‹œ์—์„œ๋Š” SchedulingConfigurer ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” SchedulerConfig ํด๋ž˜์Šค๊ฐ€ ์Šค๋ ˆ๋“œ ํ’€์˜ ํฌ๊ธฐ๋ฅผ 10์œผ๋กœ ์„ค์ •ํ•˜๊ณ , ์Šค๋ ˆ๋“œ ์ด๋ฆ„์˜ ์ ‘๋‘์‚ฌ๋ฅผ "my-scheduler-"๋กœ ์„ค์ •ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, ์Šค์ผ€์ค„๋ง ์ž‘์—…์€ "my-scheduler-" ์ ‘๋‘์‚ฌ๋ฅผ ๊ฐ€์ง„ 10๊ฐœ์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์‹คํ–‰ ๊ฒฐ๊ณผ

์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด taskA์™€ taskB๊ฐ€ ๊ฐ๊ฐ ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ์ˆ˜ํ–‰๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ์™€๋Š” ๋‹ฌ๋ฆฌ taskA์˜ ํ•œ ๋ฒˆ์˜ ์‹คํ–‰์ด ๋๋‚˜์•ผ ๊ทธ๋‹ค์Œ taskA์˜ ์‹คํ–‰์ด ์‹œ์ž‘๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด, ๋™์ผํ•œ ์ž‘์—…์— ๋Œ€ํ•ด์„œ๋Š” ์ˆœ์ฐจ์ ์ธ ์‹คํ–‰์„ ๋ณด์žฅํ•˜๋ฉด์„œ๋„, ๋‹ค๋ฅธ ์ž‘์—…๋“ค์— ๋Œ€ํ•ด์„œ๋Š” ๋ณ‘๋ ฌ์ ์ธ ์‹คํ–‰์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ์Šค์ผ€์ค„๋ง ์„ค์ •์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
 

 

3. TaskScheduler ๋นˆ ๋“ฑ๋ก

TaskScheduler๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์„ค์ • ๋ฐฉ์‹์ด ๊ฐ„๋‹จํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฐฉ๋ฒ•๋„ SchedulingConfigurer์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์Šค์ผ€์ค„๋ง ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•˜๊ณ , ์Šค๋ ˆ๋“œ ํ’€์˜ ํฌ๊ธฐ์™€ ์Šค๋ ˆ๋“œ ์ด๋ฆ„์˜ ์ ‘๋‘์‚ฌ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์Šค์ผ€์ค„๋ง ์„ค์ •์„ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ์œ„์ž„ํ•˜์—ฌ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Configuration
@EnableScheduling
public class SchedulerConfig {
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        
        scheduler.setPoolSize(10);
        scheduler.setThreadNamePrefix("my-scheduler-");
        scheduler.initialize();
        
        return scheduler;
    }
}

์œ„ ์ฝ”๋“œ์—์„œ๋Š” ThreadPoolTaskScheduler๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์Šค๋ ˆ๋“œ ํ’€์˜ ํฌ๊ธฐ๋ฅผ 10์œผ๋กœ ์„ค์ •ํ•˜๋ฉฐ, ์Šค๋ ˆ๋“œ ์ด๋ฆ„์˜ ์ ‘๋‘์‚ฌ๋ฅผ "my-scheduler-"๋กœ ์„ค์ •ํ•œ ํ›„ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ SchedulingConfigurer ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ œ๊ณตํ•˜์ง€๋งŒ, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์Šค์ผ€์ค„๋ง ์„ค์ •์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ๋ณ„๋„์˜ ์„ค์ • ์—†์ด๋„ ์—ฌ๋Ÿฌ ์Šค์ผ€์ค„๋ง ์ž‘์—…์„ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ "๋ณ„๋„์˜ ์„ค์ •"์ด๋ž€, SchedulingConfigurer๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์—์„œ ํ•„์š”ํ•œ configureTasks ๋ฉ”์„œ๋“œ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. TaskScheduler ๋นˆ ๋“ฑ๋ก ๋ฐฉ์‹์—์„œ๋Š” ์ด๋Ÿฐ ์ถ”๊ฐ€์ ์ธ ์ž‘์—… ์—†์ด๋„ ์Šคํ”„๋ง์ด ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์‹คํ–‰ ๊ฒฐ๊ณผ

์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜๋ฉด, SchedulingConfigurer๋ฅผ ๊ตฌํ˜„ํ–ˆ์„ ๋•Œ์™€ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

์ •๋ฆฌ

์Šคํ”„๋ง์˜ @Scheduled ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ”์„œ๋“œ์— ์Šค์ผ€์ค„๋ง ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๊ธฐ๋ณธ ์„ค์ •์œผ๋กœ๋Š” ๋ชจ๋“  ์ž‘์—…์ด ํ•œ ์Šค๋ ˆ๋“œ์—์„œ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Async ์ฒ˜๋ฆฌ, SchedulingConfigurer ๊ตฌํ˜„, TaskScheduler ๋นˆ ๋“ฑ๋ก ๋“ฑ์˜ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šค์ผ€์ค„๋ง ๋™์ž‘์„ ์„ธ๋ถ€์ ์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ ์ž‘์—…๋“ค์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜, ์Šค์ผ€์ค„๋ง ์„ค์ •์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 


GitHub

LinkedIn

GitHub

LinkedIn