programing

스프링 스케줄링 작업 - 한 번만 실행

skycolor 2023. 8. 25. 23:27
반응형

스프링 스케줄링 작업 - 한 번만 실행

Spring service method는 정확히 지정된 시간에 한 번만 스케줄이 가능합니까?예를 들어, 현재 시간은 오후 2시이지만 액션 버튼을 누르면 오후 8시에 서비스가 시작되기를 원합니다.저는 @Scheduled 주석에 익숙하며 주기적으로 실행되지 않도록 cron 식을 작성하는 방법을 잘 모르겠습니다.이것@Scheduled(cron = "0 0 20 * * ?")매일 오후 8시에 화재가 발생합니다.
좋은 의견이라도 있나?

죽은 자의 해결책:

@Scheduled(initialDelay = 1000 * 30, fixedDelay=Long.MAX_VALUE)

다시 불이 나기 전에 당신은 죽을 겁니다.

Spring's Task Scheduler의 구현 중 하나를 사용할 수 있습니다.아래에 너무 많은 구성이 필요하지 않은 예제(싱글 스레드의 예약된 실행자를 래핑하는 동시 작업 스케줄러)를 제공했습니다)를 제공했습니다.

가장 간단한 방법은 실행 가능한 예약과 날짜만 사용하는 예약입니다.그러면 지정된 시간 에 작업이 한 번 실행됩니다.다른 모든 메서드는 반복적으로 실행되는 태스크를 예약할 수 있습니다.

작업 실행 및 스케줄링에 대해 자세히 알아보기

간단한 작업 예제:

private TaskScheduler scheduler;

Runnable exampleRunnable = new Runnable(){
    @Override
    public void run() {
        System.out.println("Works");
    }
};

@Async
public void executeTaskT() {
    ScheduledExecutorService localExecutor = Executors.newSingleThreadScheduledExecutor();
    scheduler = new ConcurrentTaskScheduler(localExecutor);

    scheduler.schedule(exampleRunnable,
            new Date(1432152000000L));//today at 8 pm UTC - replace it with any timestamp in miliseconds to text
}

...

executeTaskT() //call it somewhere after the spring application has been configured

참고:

@Scheduled 및 @Async 주석에 대한 지원을 활성화하려면 @Configuration 클래스 중 하나에 @EnableScheduling 및 @EnableAsync를 추가합니다.


업데이트 - 스케줄링된 작업 취소

TaskScheduler의 Scheduled Future 메서드는 취소할 수 있는 지연된 결과가 포함된 작업인 Scheduled Future를 반환합니다.

따라서 취소하려면 예약된 작업(즉, 예약된 미래 반환 개체 유지)을 처리해야 합니다.

작업을 취소하기 위해 위의 코드가 변경되었습니다.

  1. executeTaskT 메서드 외부의 ScheduledFuture를 선언합니다.
    private ScheduledFuture scheduledFuture;
  1. 다음과 같이 반환 개체를 유지하도록 예약하도록 통화를 수정합니다.
    scheduledFuture = scheduler.schedule(exampleRunnable,
                    new Date(1432152000000L));
  1. 코드의 어딘가에 있는 예약된 Future 개체에 대해 취소 호출
    boolean mayInterruptIfRunning = true;
    scheduledFuture.cancel(mayInterruptIfRunning);

정기 트리거를 다음과 같이 연장할 수 있습니다. 이 트리거는 마지막 완료를 확인합니다.시간: 작업이 이전에 실행된 적이 없으면 null입니다.지정된 시간에 한 번만 태스크를 실행하려는 경우 이 버전을 사용해 볼 수 있습니다.

class RunOnceTrigger extends PeriodicTrigger {
    public RunOnceTrigger(long period) {
        super(period);
        setInitialDelay(period);
    }

    @Override
    public Date nextExecutionTime(TriggerContext triggerContext) {
        if(triggerContext.lastCompletionTime() == null) {   // hasn't executed yet
            return super.nextExecutionTime(triggerContext);
        }
        return null;
    }
}

생성하지 않으려면ScheduledExecutorService그리고.ConcurrentTaskScheduler모든 메서드 호출에서 초기화하는 것이 편리합니다.TaskScheduler서비스 생성 시, 예:

private final TaskScheduler taskScheduler = 
              new ConcurrentTaskScheduler(Executors.newScheduledThreadPool(10));

@Async작업을 예약하고 메서드를 종료하기 때문에 의미가 없습니다.

Spring 6.0.10/Spring Boot 3.0.8 이후의 ThreadPool Task Scheduler 구현은 모든 지연을 나노초로 변환합니다.불행하게도 주석은@Scheduled를 사용합니다.TimeUnit 중, 실행 중, 실행 중

@Scheduled(initialDelay = ..., fixedDelay=Long.MAX_VALUE)

을 "으"로 해제합니다.java.lang.ArithmeticException: long overflow 변환하기 때문입니다.Long.MAX_VALUE밀리초는 오래 걸리지 않을 것입니다.

쉬운 해결 방법은 나노초를 선택하여 고정 지연을 제한하는 것입니다.

@Scheduled(initialDelay = 1000 * 30, fixedDelay=Long.MAX_VALUE, timeUnit = TimeUnit.NANOSECONDS )

이렇게 하면 지연 시간이 3억 년에서 300년 미만으로 줄어들지만, 가장 혼란스러운 배치 일정에도 충분할 것입니다.

이미 진행 중인 수정 사항이 있습니다. https://github.com/spring-projects/spring-framework/commit/599ac58baa049d0075edf802cf056ffa7112fc87

질문의 제목은 원샷 작업 러너가 필요하다는 것을 의미합니다.다음은 지연 후 한 번 실행되는 작업 클래스입니다(또는 OP 요청에 따라 에폭으로 대체).또한 application.properties/yaml 또는 프로필에서 활성화/비활성화할 수 있습니다.

@Slf4j
@Component
@ConditionalOnProperty({ "app.scheduler.onceRunner" })
public class RunOnceTask
{
    public static final long INITIAL_DELAY_SECS = 5L;  // As you wish

    @Autowired
    @Qualifier("mainTaskScheduler") // You could set a custom TPTS to set threads, name, etc.
    private ThreadPoolTaskScheduler poolTaskScheduler;

    @PostConstruct
    public void run()
    {
        poolTaskScheduler.scheduleWithFixedDelay( () -> {

            log.info( "Once-runner ran once" );

        }, new Date( System.currentTimeMillis() + (INITIAL_DELAY_SECS * 1000) ), Long.MAX_VALUE );    // Run once
    }
}

언급URL : https://stackoverflow.com/questions/30347233/spring-scheduling-task-run-only-once

반응형