할당 된 작업 중 하나가 어떤 이유로 든 실패하면 Java 실행 프로그램 서비스 중지


12

몇 가지 작업을 동시에 1 분 간격으로 1 초 간격으로 실행하는 서비스가 필요합니다.

작업 중 하나가 실패하면 서비스와 서비스와 함께 실행 된 모든 작업에 문제가 있음을 나타내는 표시가 있습니다 .1 분 후에 모든 것이 잘되면 모든 것이 잘되었다는 표시와 함께 서비스가 중지됩니다.

예를 들어, 나는 두 가지 기능을 가지고 있습니다 :

Runnable task1 = ()->{
      int num = Math.rand(1,100);
      if (num < 5){
          throw new Exception("something went wrong with this task,terminate");
      }
}

Runnable task2 = ()->{
      int num = Math.rand(1,100)
      return num < 50;
}



ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
task1schedule = scheduledExecutorService.scheduleAtFixedRate(task1, 1, 60, TimeUnit.SECONDS);
task2schedule = scheduledExecutorService.scheduleAtFixedRate(task2, 1, 60, TimeUnit.SECONDS);

if (!task1schedule || !task2schedule) scheduledExecutorService.shutdown();

이 문제를 어떻게 해결하고 가능한 한 일반적인 것을 만들어야하는지에 대한 아이디어가 있습니까?


1
실제 질문과 별개 Math.rand로 내장 API는 아닙니다. 의 구현 Runnable에는 void run정의 가 있어야합니다 . 제공된 컨텍스트에 유형 task1/2schedule이 있습니다 ScheduledFuture<?>. 실제 질문으로 이동하면 어떻게 사용 awaitTermination합니까? 당신은 그렇게 할 수 있습니다 scheduledExecutorService.awaitTermination(1,TimeUnit.MINUTES);. 또는 정상적인 완료 전에 취소 된 작업이 있는지 확인하는 방법은 무엇 if (task1schedule.isCancelled() || task2schedule.isCancelled()) scheduledExecutorService.shutdown();입니까?
Naman

2
1 분마다 반복되는 작업을 예약하는 것은 의미가 없지만 "1 분 후에 모든 것이 제대로 진행된 경우"작업을 중지하려고합니다. 두 경우 모두 실행 프로그램을 중지하기 때문에 1 분 후에 실행 프로그램을 종료하는 작업을 예약하는 것은 쉽지 않습니다. 그리고 선물은 이미 무언가 잘못되었는지 여부를 나타냅니다. 다른 유형의 지표를 원한다고 말하지 않았습니다.
Holger

답변:


8

아이디어는 태스크가 공통 오브젝트 TaskCompleteEvent로 푸시한다는 것입니다. 그들이 오류를 푸시하면 스케줄러가 중지되고 모든 작업이 중지됩니다.

"오류"및 "성공"맵에서 모든 작업 반복의 결과를 확인할 수 있습니다.

public class SchedulerTest {

    @Test
    public void scheduler() throws InterruptedException {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        TaskCompleteEvent taskCompleteEvent = new TaskCompleteEvent(scheduledExecutorService);
        Runnable task1 = () -> {
            int num = new Random().nextInt(100);
            if (num < 5) {
                taskCompleteEvent.message("task1-"+UUID.randomUUID().toString(), "Num "+num+" was obatined. Breaking all the executions.", true);
            }
        };
        Runnable task2 = () -> {
            int num = new Random().nextInt(100);
            taskCompleteEvent.message("task2-"+UUID.randomUUID().toString(), num < 50, false);
        };
        scheduledExecutorService.scheduleAtFixedRate(task1, 0, 1, TimeUnit.SECONDS);
        scheduledExecutorService.scheduleAtFixedRate(task2, 0, 1, TimeUnit.SECONDS);
        scheduledExecutorService.awaitTermination(60, TimeUnit.SECONDS);
        System.out.println("Success: "+taskCompleteEvent.getSuccess());
        System.out.println("Errors: "+taskCompleteEvent.getErrors());
        System.out.println("Went well?: "+taskCompleteEvent.getErrors().isEmpty());
    }

    public static class TaskCompleteEvent {

        private final ScheduledExecutorService scheduledExecutorService;
        private final Map<String, Object> errors = new LinkedHashMap<>();
        private final Map<String, Object> success = new LinkedHashMap<>();

        public TaskCompleteEvent(ScheduledExecutorService scheduledExecutorService) {
            this.scheduledExecutorService = scheduledExecutorService;
        }

        public synchronized void message(String id, Object response, boolean error) {
            if (error) {
                errors.put(id, response);
                scheduledExecutorService.shutdown();
            } else {
                success.put(id, response);
            }
        }

        public synchronized Map<String, Object> getErrors() {
            return errors;
        }

        public synchronized Map<String, Object> getSuccess() {
            return success;
        }

    }

}

2

다른 모든 실행중인 작업을 모니터링하는 작업을 추가 작업으로 추가하기 만하면됩니다. 모니터링 된 작업이 실패하면 암살자가 검사 할 수있는 세마포어 (플래그)를 설정해야합니다.

    ScheduledExecutorService executor = (ScheduledExecutorService) Executors.newScheduledThreadPool(2);

    // INSTANTIATE THE REMOTE-FILE-MONITOR:
    RemoteFileMonitor monitor = new RemoteFileMonitor(remotesource, localtarget);

    // THIS TimerTask PERIODICALLY TRIGGERS THE RemoteFileMonitor: 
    TimerTask remote = new TimerTask() {

        // RUN FORREST... RUN !
        public void run() {

            try { 

                kae.trace("TimerTask::run() --> Calling RemoteFileMonitor.check()");
                monitor.check();

            } catch (Exception ex) {

                // NULL TRAP: ALLOWS US TO CONTINUE AND RETRY:

            }

        }

    };

    // THIS TimerTask PERIODICALLY TRIES TO KILL THE REMOTE-FILE-MONITOR:
    TimerTask assassin = new TimerTask() {

        // WHERE DO BAD FOLKS GO WHEN THEY DIE ? 
        private final LocalDateTime death = LocalDateTime.now().plus(ConfigurationOptions.getPollingCycleTime(), ChronoUnit.MINUTES);

        // RUN FORREST... RUN !
        public void run() {

            // IS THERE LIFE AFTER DEATH ???
            if (LocalDateTime.now().isAfter(death)) {

                // THEY GO TO A LAKE OF FIRE AND FRY:
                kae.error(ReturnCode.MONITOR_POLLING_CYCLE_EXCEEDED);                   

            }

        }

    };

    // SCHEDULE THE PERIODIC EXECUTION OF THE RemoteFileMonitor: (remote --> run() monitor --> check())
    executor.scheduleAtFixedRate(remote, delay, interval, TimeUnit.MINUTES);

    // SCHEDULE PERIODIC ASSASSINATION ATTEMPTS AGAINST THE RemoteFileMonitor: (assassin --> run() --> after death --> die())
    executor.scheduleAtFixedRate(assassin, delay, 60L, TimeUnit.SECONDS);

    // LOOP UNTIL THE MONITOR COMPLETES:
    do {

        try {

            // I THINK I NEED A NAP:
            Thread.sleep(interval * 10);                

        } catch (InterruptedException e) {

            // FAIL && THEN cleanexit();
            kae.error(ReturnCode.MONITORING_ERROR, "Monitoring of the XXXXXX-Ingestion site was interrupted");

        }

        // NOTE: THE MONITOR IS SET TO 'FINISHED' WHEN THE DONE-File IS DELIVERED AND RETRIEVED:
    } while (monitor.isNotFinished());

    // SHUTDOWN THE MONITOR TASK:
    executor.shutdown();

2
수업 TimerTask은 전적으로 관련이 없습니다 ScheduledExecutorService. 그냥 구현 Runnable됩니다. 또한 특정 시간 ( ConfigurationOptions.getPollingCycleTime())에 도달 했는지 확인하기 위해 정기적 인 작업을 예약하는 것은 의미가 없습니다 . 이 ScheduledExecutorService있으니 원하는 시간 동안 작업을 예약하도록 지시 할 수 있습니다.
Holger

내가 사용한 예제의 구현은 작업이 완료되지 않은 경우 일정 시간이 지나면 실행 작업을 종료하는 것입니다. 유스 케이스는 다음과 같습니다. 원격 서버가 2 시간 내에 파일을 삭제하지 않은 경우 작업을 종료하십시오. 이것이 OP가 요청한 것입니다.
Greg Patnude

내 의견을 읽고 이해 했습니까? 그냥 교체가 아무 이유없이 낙담 한 클래스를 사용하여, 코드가 무엇을하는지는 중요하지 않습니다 TimerTask와 함께 Runnable당신은 코드가 무엇을 변경하지 않고, 문제를 해결했습니다. 또한, 그냥 사용 executor.schedule(assassin, ConfigurationOptions.getPollingCycleTime(), ChronoUnit.MINUTES);하면 원하는 시간에 한 번 실행되므로 if(LocalDateTime.now().isAfter(death))검사가 더 이상 사용되지 않습니다. 다시 말하지만, 코드가 실제로 간단하고 효율적으로 수행되는 것 외에는 코드의 기능이 변경되지 않습니다.
Holger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.