programing

@예약 주석을 사용하여 시작된 예약된 작업을 중지하는 방법은 무엇입니까?

telecom 2023. 8. 8. 20:04
반응형

@예약 주석을 사용하여 시작된 예약된 작업을 중지하는 방법은 무엇입니까?

Spring Framework의 주석을 사용하여 간단한 예약 작업을 만들었습니다.

 @Scheduled(fixedRate = 2000)
 public void doSomething() {}

이제 더 이상 필요하지 않을 때 이 작업을 중지하고 싶습니다.

이 방법을 시작할 때 하나의 조건부 플래그를 확인하는 방법이 있을 수 있지만 이 방법의 실행이 중지되지는 않습니다.

스프링이 멈추기 위해 제공하는 것이 있습니까?@Scheduled과제?

옵션 1: 포스트 프로세서 사용

예약을 중지해야 하는 빈에 대해 제공하고 명시적으로 호출합니다.


옵션 2: 대상 원두의 미래 지도 유지

private final Map<Object, ScheduledFuture<?>> scheduledTasks =
        new IdentityHashMap<>();

@Scheduled(fixedRate = 2000)
public void fixedRateJob() {
    System.out.println("Something to be done every 2 secs");
}

@Bean
public TaskScheduler poolScheduler() {
    return new CustomTaskScheduler();
}

class CustomTaskScheduler extends ThreadPoolTaskScheduler {

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
        ScheduledFuture<?> future = super.scheduleAtFixedRate(task, period);

        ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
        scheduledTasks.put(runnable.getTarget(), future);

        return future;
    }

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {
        ScheduledFuture<?> future = super.scheduleAtFixedRate(task, startTime, period);

        ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
        scheduledTasks.put(runnable.getTarget(), future);

        return future;
    }
}

빈에 대한 예약을 중지해야 할 경우 맵을 조회하여 해당하는 것을 가져오고 명시적으로 취소할 수 있습니다.

다음은 예약된 모든 실행 작업을 중지, 시작 및 나열할 수 있는 예입니다.

@RestController
@RequestMapping("/test")
public class TestController {

private static final String SCHEDULED_TASKS = "scheduledTasks";

@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;

@Autowired
private ScheduledTasks scheduledTasks;

@Autowired
private ObjectMapper objectMapper;

@GetMapping(value = "/stopScheduler")
public String stopSchedule(){
    postProcessor.postProcessBeforeDestruction(scheduledTasks, SCHEDULED_TASKS);
    return "OK";
}

@GetMapping(value = "/startScheduler")
public String startSchedule(){
    postProcessor.postProcessAfterInitialization(scheduledTasks, SCHEDULED_TASKS);
    return "OK";
}

@GetMapping(value = "/listScheduler")
public String listSchedules() throws JsonProcessingException{
    Set<ScheduledTask> setTasks = postProcessor.getScheduledTasks();
    if(!setTasks.isEmpty()){
        return objectMapper.writeValueAsString(setTasks);
    }else{
        return "No running tasks !";
    }
 }
}

얼마 전 프로젝트에서 모든 구성 요소가 새 예약된 작업을 만들거나 스케줄러(모든 작업)를 중지할 수 있어야 한다는 요구 사항이 있었습니다.그래서 저는 이런 것을 했습니다.

@Configuration
@EnableScheduling
@ComponentScan
@Component
public class CentralScheduler {

    private static AnnotationConfigApplicationContext CONTEXT = null;

    @Autowired
    private ThreadPoolTaskScheduler scheduler;

    public static CentralScheduler getInstance() {
        if (!isValidBean()) {
            CONTEXT = new AnnotationConfigApplicationContext(CentralScheduler.class);
        }

        return CONTEXT.getBean(CentralScheduler.class);
    }

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        return new ThreadPoolTaskScheduler();
    }

    public void start(Runnable task, String scheduleExpression) throws Exception {
        scheduler.schedule(task, new CronTrigger(scheduleExpression));
    }

    public void start(Runnable task, Long delay) throws Exception {
        scheduler.scheduleWithFixedDelay(task, delay);
    }

    public void stopAll() {
        scheduler.shutdown();
        CONTEXT.close();
    }

    private static boolean isValidBean() {
        if (CONTEXT == null || !CONTEXT.isActive()) {
            return false;
        }

        try {
            CONTEXT.getBean(CentralScheduler.class);
        } catch (NoSuchBeanDefinitionException ex) {
            return false;
        }

        return true;
    }
}

그래서 저는 다음과 같은 것들을 할 수 있습니다.

Runnable task = new MyTask();
CentralScheduler.getInstance().start(task, 30_000L);
CentralScheduler.getInstance().stopAll();

어떤 이유에서인지, 저는 동시성에 대해 걱정할 필요 없이 그것을 했다는 것을 명심하세요.그렇지 않으면 동기화가 있어야 합니다.

@Mahesh의 옵션 1의 작업 예제 구현, 사용ScheduledAnnotationBeanPostProcessor.postProcessBeforeDestruction(bean, beanName).

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;


public class ScheduledTaskExample implements ApplicationContextAware, BeanNameAware
{

    private ApplicationContext applicationContext;
    private String             beanName;

    @Scheduled(fixedDelay = 1000)
    public void someTask()
    {
        /* Do stuff */

        if (stopScheduledTaskCondition)
        {
            stopScheduledTask();
        }
    }

    private void stopScheduledTask()
    {
        ScheduledAnnotationBeanPostProcessor bean = applicationContext.getBean(ScheduledAnnotationBeanPostProcessor.class);
        bean.postProcessBeforeDestruction(this, beanName);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
    {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String beanName)
    {
        this.beanName = beanName;
    }
}

이 질문에는 약간의 모호함이 있습니다.

  1. "이 작업을 중지"라고 하면 나중에 복구할 수 있는 방식으로 중지할 것을 의미합니까(예, 그렇다면 프로그래밍 방식으로 동일한 앱 내에서 발생하는 조건 사용)?아니면 외부 조건?)
  2. 같은 맥락에서 다른 작업을 실행하고 있습니까? (작업이 아닌 전체 앱을 종료할 수 있습니다.) -- 액추에이터를 사용할 수 있습니다.이 시나리오에서 끝점 종료

복구 가능한 방식으로 동일한 앱 내에서 발생할 수 있는 조건을 사용하여 작업을 종료하려고 하는 것이 가장 좋습니다.저는 이 가정을 바탕으로 답변을 시도하겠습니다.

이것은 제가 생각할 수 있는 가장 간단한 해결책입니다. 하지만 저는 중첩된 경우보다는 조기 반환과 같은 몇 가지 개선을 할 것입니다.

@Component
public class SomeScheduledJob implements Job {

    private static final Logger LOGGER = LoggerFactory.getLogger(SomeScheduledJob.class);

    @Value("${jobs.mediafiles.imagesPurgeJob.enable}")
    private boolean imagesPurgeJobEnable;

    @Override
    @Scheduled(cron = "${jobs.mediafiles.imagesPurgeJob.schedule}")
    public void execute() {

        if(!imagesPurgeJobEnable){
            return;
        }
        Do your conditional job here...
   }

위 코드에 대한 속성

jobs.mediafiles.imagesPurgeJob.enable=true or false
jobs.mediafiles.imagesPurgeJob.schedule=0 0 0/12 * * ?

제가 아직 찾지 못한 또 다른 접근법입니다.간편하고, 투명하며, 나사산이 안전합니다.

  1. 구성 클래스에 주석을 추가합니다.

    @EnableScheduling

  2. 예약된 작업을 시작/중지해야 하는 클래스의 다음 단계:

    @Autowired TaskScheduler taskScheduler;

  3. 필드 설정:

     private ScheduledFuture yourTaskState;
     private long fixedRate = 1000L;
    
  4. 스케줄링된 작업을 실행하는 내부 클래스를 만듭니다. 예:

     class ScheduledTaskExecutor implements Runnable{
         @Override
         public void run() {
           // task to be executed
         }
     }
    
  5. 시작() 메서드 추가:

     public void start(){
         yourTaskState = taskScheduler.scheduleAtFixedRate(new ScheduledTaskExecutor(), fixedRate);
     }
    
  6. stop() 메서드 추가:

     public void stop(){
         yourTaskState.cancel(false);
     }
    

작업 스케줄러는 cron 또는 delay와 같은 다른 일반적인 스케줄링 방법을 제공합니다.

"ScheduledFutureisCancelled();

미니멀리스트 답변:
된 @1은 콩에 할 수 . @mahesh 1은 다음과 같습니다.

@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;

@Scheduled(fixedRate = 2000)
public void doSomething() {}

public void stopThis() {
    postProcessBeforeDestruction(this, "")
}

또는 모든 콩에 대한 모든 작업을 취소할 수 있습니다.

@Autowired
private ThreadPoolTaskScheduler scheduler;

@Scheduled(fixedRate = 2000)
public void doSomething() {}

public void stopAll() {
    scheduler.shutdown();
}

이전의 응답자 여러분, 저를 위해 이 문제를 해결해 주셔서 감사합니다.

예정된

스프링 공정 시Scheduled이 달린 각 방법을 합니다.

private final Map<Object, Set<ScheduledTask>> scheduledTasks =
        new IdentityHashMap<Object, Set<ScheduledTask>>(16);

취소

반복적으로 예약된 작업을 취소하려면 다음과 같이 하면 됩니다(여기 내 레포에서 실행 가능한 데모가 있습니다).

@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;
@Autowired
private TestSchedule testSchedule;

public void later() {
    postProcessor.postProcessBeforeDestruction(test, "testSchedule");
}

알려드립니다

그것은 이 콩들의 것을 찾을 것입니다.ScheduledTask그리고 하나씩 취소합니다.해야 할 중인 입니다.postProcessBeforeDestruction출처 표시).

    synchronized (this.scheduledTasks) {
        tasks = this.scheduledTasks.remove(bean); // remove from future running
    }
    if (tasks != null) {
        for (ScheduledTask task : tasks) {
            task.cancel(); // cancel current running method
        }
    }

아래와 같이 사용자 지정 주석을 정의합니다.

@Documented
@Retention (RUNTIME)
@Target(ElementType.TYPE)
public @interface ScheduledSwitch {
    // do nothing
}

클래스 구현 org.springframework.scheduling.notation을 정의합니다.예약된 주석 BeanPostProcessor입니다.

public class ScheduledAnnotationBeanPostProcessorCustom 
    extends ScheduledAnnotationBeanPostProcessor {

    @Value(value = "${prevent.scheduled.tasks:false}")
    private boolean preventScheduledTasks;

    private Map<Object, String> beans = new HashMap<>();

    private final ReentrantLock lock = new ReentrantLock(true);

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        ScheduledSwitch switch = AopProxyUtils.ultimateTargetClass(bean)
            .getAnnotation(ScheduledSwitch.class);
        if (null != switch) {
            beans.put(bean, beanName);
            if (preventScheduledTasks) {
                return bean;
            }
        }
        return super.postProcessAfterInitialization(bean, beanName);
    }

    public void stop() {
        lock.lock();
        try {
            for (Map.Entry<Object, String> entry : beans.entrySet()) {
                postProcessBeforeDestruction(entry.getKey(), entry.getValue());
            }
        } finally {
            lock.unlock();
        }
    }

    public void start() {
        lock.lock();
        try {
            for (Map.Entry<Object, String> entry : beans.entrySet()) {
                if (!requiresDestruction(entry.getKey())) {
                    super.postProcessAfterInitialization(
                        entry.getKey(), entry.getValue());
                }
            }
        } finally {
            lock.unlock();
        }
    }

}

예약된 주석 BeanPostProcessor bean을 사용자 지정 bean 구성으로 바꿉니다.

@Configuration
public class ScheduledConfig {

    @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationBeanPostProcessor() {
        return new ScheduledAnnotationBeanPostProcessorCustom();
    }

}

@ScheduledTasks를 방지하거나 중지할 빈에 @ScheduledSwitch 주석을 추가합니다.

용사를 합니다.@conditional조건 메소드의 값이 사실인지 확인하는 데 도움이 됩니까?스케줄러를 실행합니다.그렇지 않으면 뛰지 마.

그 인터페이스를 .matches방법

public class MyCondition implements Condition{

public boolean matches(ConditionContext context, AnnotatedTypeMetaData metadata) {

// here implement your condition using if-else or checking another object 
// or call another method that can return boolean value
//return boolean value : true or false 

return true;
}
}

다음 " " " " " " " " " " " " " " 를 합니다.@Scheduled

@Service
@Conditional(value = MyCondition.class)
// this Service will only run if the condition is true 
public class scheduledTask {


// the @Scheduled method should be void
@Scheduled(fixedRate= 5000)
public void task(){
System.out.println(" This is scheduled task started....");
}

}

이것은 확실히 저에게 효과가 있었습니다.

import com.google.common.collect.Maps;
import org.redisson.liveobject.misc.ClassUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTask;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

import static java.util.Collections.emptySet;
import com.google.common.collect.Maps;
import org.redisson.liveobject.misc.ClassUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTask;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

import static java.util.Collections.emptySet;

/**
 * @author uhfun
 */
@Component
public class ConfigurableScheduler {
    private final InnerScheduledAnnotationBeanPostProcessor postProcessor;

    public ConfigurableScheduler(InnerScheduledAnnotationBeanPostProcessor postProcessor) {
        this.postProcessor = postProcessor;
    }

    public void registerScheduleTask(String cron, Method method, Object target) {
        Map<String, Object> attributes = Maps.newHashMap();
        attributes.put("cron", cron);
        Scheduled scheduled = AnnotationUtils.synthesizeAnnotation(attributes, Scheduled.class, null);
        postProcessor.registerScheduleTask(scheduled, method, target);
    }

    public void unregister(String cron, Object target) {
        postProcessor.unregister(target, cron);
    }

    @Component
    public static class InnerScheduledAnnotationBeanPostProcessor extends ScheduledAnnotationBeanPostProcessor {

        private final Map<Object, Set<ScheduledTask>> scheduledTasksMap;

        public InnerScheduledAnnotationBeanPostProcessor() {
            scheduledTasksMap = ClassUtils.getField(this, "scheduledTasks");
        }

        public void registerScheduleTask(Scheduled scheduled, Method method, Object bean) {
            super.processScheduled(scheduled, method, bean);
        }

        public void unregister(Object bean, String cron) {
            synchronized (scheduledTasksMap) {
                Set<ScheduledTask> tasks = getScheduledTasks();
                for (ScheduledTask task : tasks) {
                    if (task.getTask() instanceof CronTask
                            && ((CronTask) task.getTask()).getExpression().equals(cron)) {
                        task.cancel();
                        scheduledTasksMap.getOrDefault(bean, emptySet()).remove(task);
                    }
                }
            }
        }
    }
}

를 사용하는 것은 어떻습니까?System.exit(1)이것은 간단하고 효과적입니다.

언급URL : https://stackoverflow.com/questions/44644141/how-to-stop-a-scheduled-task-that-was-started-using-scheduled-annotation

반응형