๊ด€๋ฆฌ ๋ฉ”๋‰ด

Jin's Dev Story

[SpringBoot] ์Šค์ผ€์ค„๋ง ๋ณธ๋ฌธ

Web & Android/SpringBoot

[SpringBoot] ์Šค์ผ€์ค„๋ง

woojin._. 2023. 10. 13. 09:59

๐Ÿ’ก ScheduledExecutorService๊ณผ SpringBoot์˜ @Scheduled๋กœ ์Šค์ผ€์ค„๋ง์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ScheduledExecutorService์€ ์ธ์ˆ˜์žˆ๋Š” ๋ฉ”์„œ๋“œ์—๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ @Scheduled๋Š” ์ธ์ˆ˜์žˆ๋Š” ๋ฉ”์„œ๋“œ์— ์ ์šฉ์ด ๋ถˆ๊ฐ€ํ•˜๋‹ค.

ScheduledExecutorService

  • Java 1.5๋ถ€ํ„ฐ ์ง€์›๋˜๋Š” ์Šค์ผ€์ค„๋ง์ด ๊ฐ€๋Šฅํ•œ Executor ์„œ๋น„์Šค๋กœ ์ธํ„ฐํŽ˜์ด์Šค์ด๋ฉฐ, ๊ตฌํ˜„์ฒด๋กœ ScheduledThreadPoolExecutor()๊ฐ€ ์žˆ๋‹ค.
  • ์˜ˆ์ œ ์ฝ”๋“œ
  @Service
  @RequiredArgsConstructor
  @Transactional
  public class BattleRivalService {

    private final BattleRepository battleRepository;
    private final BattleRivalRepository battleRivalRepository;
    private final TeamRepository teamRepository;
    private final BattleService battleService;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public BattleRivalResponseDTO saveBattleRival(Battle battle, TeamMember teamMember) {
            LocalDate lc = LocalDate.now();
            LocalDateTime finishTime = LocalDateTime.now().plusDays(7);

            BattleRival result = battleRivalRepository.findByTeamId(teamMember.getTeam().getId());

            // ํŒ€์ด ๋Œ€๊ฒฐ์— ์ฐธ์—ฌํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ
            if(result == null) {
                BattleRival battleRival = new BattleRival(battle, teamMember.getTeam());
                battleRivalRepository.save(battleRival);

                battle.update(lc);
                battleRepository.save(battle);

                Team team = teamRepository.findById(battle.getBattleRivals().get(0).getTeam().getId()).orElse(null);
                team.updateState("๋Œ€๊ฒฐ ์ง„ํ–‰ ์ค‘");
                teamRepository.save(team);

                team = teamRepository.findById(teamMember.getTeam().getId()).orElse(null);
                team.updateState("๋Œ€๊ฒฐ ์ง„ํ–‰ ์ค‘");
                teamRepository.save(team);

                scheduler.schedule(() -> {
                   try {
                       battleService.finishBattle(battle.getId());
                   } catch (ParseException e) {
                       throw new RuntimeException(e);
                   }
                }, ChronoUnit.SECONDS.between(LocalDateTime.now(), finishTime), TimeUnit.SECONDS);

                return BattleRivalResponseDTO.builder()
                        .teamId(battleRival.getTeam().getId())
                        .teamName(battleRival.getTeam().getName())
                        .peopleNum(battleRival.getTeam().getPeopleNum())
                        .step(battleRival.getStep())
                        .build();
            } else {
                // ํŒ€์ด ๋Œ€๊ฒฐ์— ์ฐธ์—ฌํ•œ ๊ฒฝ์šฐ
                return null;
            }
        }

 

@Scheduled

  • ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ์Šค์ผ€์ค„๋ง ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • @EnableScheduling ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์Šค์ผ€์ค„๋ง์„ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค๋Š” ๊ฒƒ์„ ๋ช…์‹œํ•˜๊ณ , @Scheduled ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์Šค์ผ€์ค„๋ง ํ•  ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
  @EnableScheduling
  @SpringBootApplication
  public class WalkingMateBackApplication {

      public static void main(String[] args) {
          SpringApplication.run(WalkingMateBackApplication.class, args);
      }

  }
  • ์‹ค์ œ ์Šค์ผ€์ค„๋ง ์ž‘์—…ํ•  ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.
    @Service
    public class SchedulerService {
     
        @Scheduled(fixedDelay = 1000) // 1์ดˆ๋งˆ๋‹ค ์‹คํ–‰
        public void run() {
            System.out.println("Hello CoCo World!);
        }
    }
    @Component(@Service ๋“ฑ) ์ฆ‰, ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋ก๋œ ํด๋ž˜์Šค์—ฌ์•ผ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ๋‘ ๊ฐœ์˜ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ ๊ธฐ๋งŒ ํ•˜๋ฉด ์„ค์ •์€ ๋์ด๊ณ  ๋‹ค์Œ ๊ทœ์น™์„ ์ง€ํ‚ค๋ฉฐ ์Šค์ผ€์ค„๋Ÿฌ ๋ฉ”์†Œ๋“œ๋ฅผ ๋งŒ๋“ค์ž.
    • @Scheduled ๊ทœ์น™
      - Method๋Š” void ํƒ€์ž…์œผ๋กœ
      - Method๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ ์‚ฌ์šฉ ๋ถˆ๊ฐ€

@Scheduled ์†์„ฑ

fixedDelay : milliseconds ๋‹จ์œ„๋กœ, ์ด์ „ Task์˜ ์ข…๋ฃŒ ์‹œ์ ์œผ๋กœ๋ถ€ํ„ฐ ์ •์˜๋œ ์‹œ๊ฐ„๋งŒํผ ์ง€๋‚œ ํ›„ Task๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

@Scheduled(fixedDelay = 1000)
public void run() {
    System.out.println("Hello CoCo World!");
}

fixedDelayString : fixedDelay์™€ ๊ฐ™์€๋ฐ ๋ฌธ์ž์—ด๋กœ ๊ฐ’์„ ํ‘œํ˜„ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

@Scheduled(fixedDelay = "1000")
public void run() {
    System.out.println("Hello CoCo World!");
}

fixedRate : milliseconds ๋‹จ์œ„๋กœ, ์ด์ „ Task์˜ ์‹œ์ž‘ ์‹œ์ ์œผ๋กœ๋ถ€ํ„ฐ ์ •์˜๋œ ์‹œ๊ฐ„๋งŒํผ ์ง€๋‚œ ํ›„ Task๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

@Scheduled(fixedRate = 1000)
public void run() {
        System.out.println("Hello CoCo World!");    
}

fixedRateString : fixedRate์™€ ๊ฐ™์€๋ฐ ๋ฌธ์ž์—ด๋กœ ๊ฐ’์„ ํ‘œํ˜„ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

@Scheduled(fixedRateString = "1000")
public void run() {
    System.out.println("Hello CoCo World!");

 

โ€ป fixedDelay vs fixedRate


fixedRate๋Š” ์ž‘์—… ์ˆ˜ํ–‰์‹œ๊ฐ„๊ณผ ์ƒ๊ด€์—†์ด ์ผ์ • ์ฃผ๊ธฐ๋งˆ๋‹ค ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด๊ณ ,

fixedDelay๋Š” (์ž‘์—… ์ˆ˜ํ–‰ ์‹œ๊ฐ„์„ ํฌํ•จํ•˜์—ฌ) ์ž‘์—…์„ ๋งˆ์นœ ํ›„๋ถ€ํ„ฐ ์ฃผ๊ธฐ ํƒ€์ด๋จธ๊ฐ€ ๋Œ์•„ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

initialDelay : ์Šค์ผ€์ค„๋Ÿฌ์—์„œ ๋ฉ”์†Œ๋“œ๊ฐ€ ๋“ฑ๋ก๋˜์ž๋งˆ์ž ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ดˆ๊ธฐ ์ง€์—ฐ์‹œ๊ฐ„์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

    @Scheduled(fixedRate = 5000, initialDelay = 3000)
    public void run() {
        System.out.println("Hello CoCo World!");
    }

์œ„์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด 3์ดˆ์˜ ๋Œ€๊ธฐ์‹œ๊ฐ„(initialDelay) ํ›„์— 5์ดˆ(fixedRate)๋งˆ๋‹ค "Hello CoCo World!"๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์ž‘์—…์„ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ˆ˜ํ–‰ํ•ด์ค€๋‹ค.

initialDelayString : ์œ„์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ฌธ์ž์—ด๋กœ ๊ฐ’์„ ํ‘œํ˜„ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

    @Scheduled(fixedRate = 5000, initialDelayString = "3000")
    public void run() {
        System.out.println("Hello CoCo World!");

cron : Cron ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์—…์„ ์˜ˆ์•ฝํ•  ์ˆ˜ ์žˆ๋‹ค.

    @Scheduled(cron = "* * * * * *")
    public void run() {
        System.out.println("Hello CoCo World!");
    }

์ฒซ ๋ฒˆ์งธ * ๋ถ€ํ„ฐ ์ดˆ(0-59), ๋ถ„(0-59), ์‹œ๊ฐ„(0-23), ์ผ(1-31), ์›”(1-12), ์š”์ผ(0-6) (0: ์ผ, 1: ์›”, 2:ํ™”, 3:์ˆ˜, 4:๋ชฉ, 5:๊ธˆ, 6:ํ† ), Spring @Scheduled cron์€ 6์ž๋ฆฌ ์„ค์ •๋งŒ ํ—ˆ์šฉํ•˜๋ฉฐ ์—ฐ๋„ ์„ค์ •์„ ํ•  ์ˆ˜ ์—†๋‹ค.

  • Cron ํ‘œํ˜„์‹
      • : ๋ชจ๋“  ์กฐ๊ฑด(๋งค์‹œ, ๋งค์ผ, ๋งค์ฃผ์ฒ˜๋Ÿผ ์‚ฌ์šฉ)์„ ์˜๋ฏธ
    • ? : ์„ค์ • ๊ฐ’ ์—†์Œ (๋‚ ์งœ์™€ ์š”์ผ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ)
    • '-' : ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•  ๋•Œ
    • , : ์—ฌ๋Ÿฌ ๊ฐ’์„ ์ง€์ •ํ•  ๋•Œ
    • / : ์ฆ๋ถ„๊ฐ’, ์ฆ‰ ์ดˆ๊ธฐ๊ฐ’๊ณผ ์ฆ๊ฐ€์น˜ ์„ค์ •์— ์‚ฌ์šฉ
    • L : ๋งˆ์ง€๋ง‰ - ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ”์œ„์˜ ๋งˆ์ง€๋ง‰ ๊ฐ’ ์„ค์ • ์‹œ ์‚ฌ์šฉ (๋‚ ์งœ์™€ ์š”์ผ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ)
    • W : ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํ‰์ผ(weekday)์„ ์„ค์ •ํ•  ๋•Œ
      • ์˜ˆ) 10W
        - 10์ผ์ด ํ‰์ผ ์ผ ๋•Œ : 10์ผ์— ์‹คํ–‰
        - 10์ผ์ด ํ† ์š”์ผ ์ผ ๋•Œ : ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํ‰์ผ์ธ ๊ธˆ์š”์ผ(9์ผ)์— ์‹คํ–‰
        - 10์ผ์ด ์ผ์š”์ผ ์ผ ๋•Œ : ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํ‰์ผ์ธ ์›”์š”์ผ(11์ผ)์— ์‹คํ–‰
    • '#' : N๋ฒˆ์งธ ์ฃผ ํŠน์ • ์š”์ผ์„ ์„ค์ •ํ•  ๋•Œ (-์š”์ผ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ)
      • ์˜ˆ) 4#2
      • ๋ชฉ์š”์ผ#2์งธ์ฃผ์— ์‹คํ–‰

zone : cron ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์‚ฌ์šฉํ•  time zone์œผ๋กœ ๋”ฐ๋กœ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ Local์˜ time zone์ด๋‹ค.

    @Scheduled(cron = "* * * * * *", zone = "Asia/Seoul")
    public void run() {
        System.out.println("Hello CoCo World!");
    }

cron ์‚ฌ์šฉ ์˜ˆ์‹œ

    // ๋งค์ผ ์˜คํ›„ 18์‹œ์— ์‹คํ–‰
    @Scheduled(cron = "0 0 18 * * *")
    public void run() {
        System.out.println("Hello CoCo World!");
    }
    
    // ๋งค๋‹ฌ 10์ผ,20์ผ 14์‹œ์— ์‹คํ–‰
    @Scheduled(cron = "0 0 14 10,20 * ?")
    public void run() {
        System.out.println("Hello CoCo World!");
    }
    
    // ๋งค๋‹ฌ ๋งˆ์ง€๋ง‰๋‚  22์‹œ์— ์‹คํ–‰
    @Scheduled(cron = "0 0 22 L * ?")
    public void run() {
        System.out.println("Hello CoCo World!");
    }
    
    // 1์‹œ๊ฐ„ ๋งˆ๋‹ค ์‹คํ–‰ ex) 01:00, 02:00, 03:00 ...
    @Scheduled(cron = "0 0 0/1 * * *")
    public void run() {
        System.out.println("Hello CoCo World!");
    }
    
    // ๋งค์ผ 9์‹œ00๋ถ„-9์‹œ55๋ถ„, 18์‹œ00๋ถ„-18์‹œ55๋ถ„ ์‚ฌ์ด์— 5๋ถ„ ๊ฐ„๊ฒฉ์œผ๋กœ ์‹คํ–‰
    @Scheduled(cron = "0 0/5 9,18 * * *")
    public void run() {
        System.out.println("Hello CoCo World!");
    }
    
    // ๋งค์ผ 9์‹œ00๋ถ„-18์‹œ55๋ถ„ ์‚ฌ์ด์— 5๋ถ„ ๊ฐ„๊ฒฉ์œผ๋กœ ์‹คํ–‰
    @Scheduled(cron = "0 0/5 9-18 * * *")
    public void run() {
        System.out.println("Hello CoCo World!");
    }
    
    // ๋งค๋‹ฌ 1์ผ 10์‹œ30๋ถ„์— ์‹คํ–‰
    @Scheduled(cron = "0 30 10 1 * *")
    public void run() {
        System.out.println("Hello CoCo World!");
    }
    
    // ๋งค๋…„ 3์›”๋‚ด ์›”-๊ธˆ 10์‹œ30๋ถ„์— ์‹คํ–‰
    @Scheduled(cron = "0 30 10 ? 3 1-5")
    public void run() {
        System.out.println("Hello CoCo World!");
    }
    
    // ๋งค๋‹ฌ ๋งˆ์ง€๋ง‰ ํ† ์š”์ผ 10์‹œ30๋ถ„์— ์‹คํ–‰
    @Scheduled(cron = "0 30 10 ? * 6L")
    public void run() {
        System.out.println("Hello CoCo World!");
    }

์ถœ์ฒ˜ : https://dev-coco.tistory.com/176

'Web & Android > SpringBoot' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[SpringBoot] REST API  (0) 2023.10.13
[SpringBoot] Bean  (0) 2023.10.13
[SpringBoot] Google ์ด๋ฉ”์ผ ์ธ์ฆ  (0) 2023.09.12
[SpringBoot] Lombok  (1) 2023.08.18
[SpringBoot] HTTP form์—์„œ put, delete ์‚ฌ์šฉ๋ฒ•  (0) 2023.08.17