Java menghentikan layanan pelaksana setelah salah satu tugas yang ditugaskan gagal karena alasan apa pun

12

Saya memerlukan beberapa jenis layanan yang akan menjalankan beberapa tugas secara bersamaan dan dalam interval 1 detik selama 1 menit.

Jika salah satu tugas gagal, saya ingin menghentikan layanan dan setiap tugas yang berjalan dengannya dengan semacam indikator bahwa ada sesuatu yang salah, jika tidak, setelah satu menit semuanya berjalan dengan baik, layanan akan berhenti dengan indikator bahwa semua berjalan dengan baik.

Sebagai contoh, saya memiliki 2 fungsi:

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();

Ada ide tentang bagaimana saya harus mengatasi ini dan membuat hal-hal yang sama generik mungkin?

totothegreat
sumber
1
Beberapa hal yang terpisah dari pertanyaan sebenarnya, Math.randbukan API bawaan. Suatu implementasi Runnableharus memiliki void rundefinisi. Jenis task1/2scheduleakan berada ScheduledFuture<?>dalam konteks yang disediakan. Pindah ke pertanyaan aktual, bagaimana cara memanfaatkannya awaitTermination? Anda bisa melakukannya sebagai scheduledExecutorService.awaitTermination(1,TimeUnit.MINUTES);. Atau, bagaimana dengan memeriksa jika salah satu tugas harus dibatalkan sebelum selesai normal: if (task1schedule.isCancelled() || task2schedule.isCancelled()) scheduledExecutorService.shutdown();?
Naman
2
Tidak ada gunanya menjadwalkan tugas yang harus diulang setiap menit, tetapi kemudian katakan, Anda ingin menghentikan tugas "jika setelah satu menit semuanya berjalan dengan baik". Karena Anda menghentikan pelaksana dalam kedua kasus tersebut, menjadwalkan tugas yang mematikan pelaksana setelah satu menit adalah sepele. Dan masa depan sudah menunjukkan apakah ada yang salah atau tidak. Anda tidak mengatakan, jenis indikator apa yang Anda inginkan.
Holger

Jawaban:

8

Idenya adalah bahwa tugas tersebut mendorong ke objek Common TaskCompleteEvent. Jika mereka mendorong kesalahan, penjadwal dihentikan dan semua tugas akan berhenti.

Anda dapat memeriksa hasil dari setiap tugas-iterasi di peta "kesalahan" dan "sukses".

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;
        }

    }

}
ravenskater
sumber
2

Anda hanya perlu menambahkan tugas tambahan yang tugasnya adalah memantau semua tugas yang sedang berjalan lainnya - dan ketika salah satu tugas yang dipantau gagal, mereka perlu mengatur semaphore (bendera) yang dapat diperiksa oleh pembunuh bayaran.

    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();
Greg Patnude
sumber
2
Kelas TimerTasksama sekali tidak terkait dengan ScheduledExecutorService; itu hanya terjadi pada implementasi Runnable. Selanjutnya, tidak masuk akal untuk menjadwalkan tugas berkala, hanya untuk memeriksa apakah waktu tertentu ( ConfigurationOptions.getPollingCycleTime()) telah tercapai. Anda punya ScheduledExecutorService, jadi Anda bisa mengatakannya untuk menjadwalkan tugas yang tepat untuk waktu yang diinginkan.
Holger
Implementasi dalam contoh yang saya gunakan adalah untuk membunuh tugas pelaksana setelah periode waktu tertentu jika tugas belum selesai. Kasus penggunaannya adalah: Jika server jauh belum menjatuhkan file dalam waktu 2 jam - matikan tugas. inilah yang diminta OP.
Greg Patnude
Apakah Anda membaca dan memahami komentar saya? Tidak masalah apa kode itu, ia menggunakan kelas berkecil hati tanpa alasan, cukup ganti TimerTaskdengan Runnabledan Anda telah memperbaiki masalahnya, tanpa mengubah apa yang dilakukan kode. Selanjutnya, cukup gunakan executor.schedule(assassin, ConfigurationOptions.getPollingCycleTime(), ChronoUnit.MINUTES);dan itu akan berjalan sekali pada waktu yang diinginkan, karenanya, if(LocalDateTime.now().isAfter(death))cek tersebut sudah usang. Sekali lagi, tidak mengubah apa yang dilakukan kode, selain melakukannya secara substansial lebih sederhana dan lebih efisien.
Holger