2010-04-20 15 views
39

To powinno być naprawdę łatwe. Używam Quartz działającego pod Apache Tomcat 6.0.18 i mam jobs.xml file, który ustawia moją zaplanowaną pracę, która działa co minutę.Kwarc: zapobieganie równoczesnym przypadkom pracy w jobs.xml

To, co chciałbym zrobić, to czy praca jest nadal uruchomiona, gdy pojawia się następny czas wyzwalania, nie chcę rozpoczynać nowego zadania, więc mogę pozwolić na ukończenie starej instancji.

Czy istnieje sposób, aby to określić w pliku jobs.xml (zapobiec jednoczesnym wystąpieniom)?

Jeśli nie, czy jest sposób, w jaki mogę udostępnić dostęp do pojedynczej pamięci w ramach implementacji mojego wniosku Job? (Czy to przez JobExecutionContext?), Więc sam mogę obsługiwać współbieżność? (I wykryć, czy poprzednia instancja jest uruchomiona)


zmiana: Po brnąc wokół w docs, oto kilka podejść Zastanawiam, ale albo nie wiem, jak je zdobyć do pracy, lub są problemy.

  1. Użyj StatefulJob. Zapobiega to równoczesnemu dostępowi ... ale nie jestem pewien, jakie inne skutki uboczne wystąpią, jeśli go użyję, również chcę uniknąć następującej sytuacji:

    Przypuśćmy, że czasy wyzwolenia byłyby co minutę, tj. Wyzwalacz # 0 = w czasie 0, wyzwalacz # 1 = 60000msec, # 2 = 120000, # 3 = 180000 itd., a wyzwalacz # 0 w czasie 0 uruchamia moje zadanie, które zajmuje 130000msec. Z prostym zadaniem, to wykona wyzwalacze # 1 i # 2, podczas gdy wyzwalacz zadania # 0 nadal działa. W przypadku funkcji StatefulJob wykona to wyzwalacze nr 1 i nr 2 w kolejności, zaraz po tym, jak numer 0 zakończy się na 130000. Nie chcę tego, chcę, aby pierwsze i drugie nie działały, a następny wyzwalacz, który uruchamia zadanie, powinien odbywa się na nr 3 (180000msec). Tak więc nadal muszę zrobić coś innego z StatefulJob, aby działał tak, jak chcę, więc nie widzę dużej korzyści z jego używania.

  2. Użyj TriggerListener, aby zwrócić wartość true z vetoJobExecution().

    Chociaż implementacja interfejsu wydaje się prosta, muszę wymyślić, jak zadeklarować jedną instancję TriggerListener. Can't find the docs for the xml file.

  3. Użyj udostępnionego obiektu bezpiecznego wątku static (na przykład semafora lub coś podobnego) należącego do mojej klasy, która implementuje zadanie.

    Nie podoba mi się pomysł używania singletonów za pomocą słowa kluczowego static pod Tomcat/Quartz, nie jestem pewien, czy są jakieś efekty uboczne. Poza tym naprawdę nie chcę, aby były prawdziwymi singletonami, tylko czymś, co wiąże się z określoną definicją stanowiska.

  4. Implementuję mój własny Trigger, który rozszerza SimpleTrigger i zawiera stan współdzielony, który może uruchomić własny TriggerListener.

    Ponownie, nie wiem, jak skonfigurować plik XML, aby użyć tego wyzwalacza zamiast standardowego <trigger><simple>...</simple></trigger>.

+8

W nowszych wersjach kwarcu, znakowanie klasy zadania wykonawcze w @DisallowConcurrentExecution zapewnia, że ​​tylko jedna klasa biegnie w każdej chwili ... – sbrattla

Odpowiedz

17

gdy praca Quartz budzi można zrobić:

JobDetail existingJobDetail = sched.getJobDetail(jobName, jobGroup); 
    if (existingJobDetail != null) { 
     List<JobExecutionContext> currentlyExecutingJobs = (List<JobExecutionContext>) sched.getCurrentlyExecutingJobs(); 
     for (JobExecutionContext jec : currentlyExecutingJobs) { 
      if(existingJobDetail.equals(jec.getJobDetail())) { 
       //String message = jobName + " is already running."; 
       //log.info(message); 
       //throw new JobExecutionException(message,false); 
      } 
     } 
     //sched.deleteJob(jobName, jobGroup); if you want to delete the scheduled but not-currently-running job 
    } 
+1

Wygląda interesująco ... musisz sprawdzić w stosunku do istniejącego JobExecutionContext, w przeciwnym razie zawsze otrzymasz wyjątek. Istnieje również wyjątek zgłoszony przez sched.getCurrentlyExecutingJobs(), który musi zostać przechwycony. –

5

Zrobiłem coś podobnego, dzięki czemu moje zajęcia prac wdrożono StatefulJob, co gwarantuje, że żadne inne zadania nie zostaną uruchomione przed zakończeniem bieżącego zadania.

nadzieję, że pomoże;)

PD: I wprowadziły go przy użyciu JBoss ... ale nie sądzę, że czyni żadnej różnicy.

+0

pomaga nieco, ale nie do końca, patrz mój komentarz # 1 powyżej. –

+1

@younghobbit, zobacz zaktualizowaną odpowiedź – Ramses

4

można ustawić pracę jako StatefulJob, a dla każdego wyzwalaczem create ustaw MisfireInstruction do pracy, aby nie strzelać, jeśli jest pominięte? Nie wiesz, jakiego rodzaju pracy używasz, ale będziesz musiał przeprowadzić pewne badania dotyczące przerw zapłonu, które są dostępne dla danego typu wyzwalacza.

Dzięki, D odpowiedź

20

dimitrisli nie jest kompletna więc tutaj jest moje.

Gdy budzi się kwarcowa praca, zwraca do ciebie swój tekst JobExecutionContext. Zakładam, że chcesz pominąć zadania z tym samym wyzwalaczem.

Dostajemy obecnie konteksty zadań i sprawdzamy, czy istnieje poprzednia instancja zadania z tym samym wyzwalaczem. W takim przypadku po prostu pomijamy z powrotem.

56

Istnieje jeszcze jedno prostsze rozwiązanie. Zadanie może otrzymać adnotację o DisallowConcurrentExecution, która uniemożliwia uruchomienie wielu jednoczesnych instancji. Zobacz dokumentację here.

Łącze wciąż pęka, więc tutaj jest odpowiednia próbka.

@DisallowConcurrentExecution 
public class ColorJob implements Job { 
+7

Adnotacja DisallowConcurrentExecution powoduje umieszczenie w kolejce bieżącej instancji w celu wykonania aż do zakończenia poprzedniej instancji. Czy istnieje sposób na usunięcie bieżącej instancji z wykonania, jeśli wystąpienie zadania już działa? –

+2

Pamiętaj jednak, że ograniczenie dotyczy tylko zadań z tym samym JobKey (co zwykle ma miejsce). Zlecenia z różnymi JobKeys można nadal wykonywać jednocześnie, nawet jeśli są zaimplementowane w tej samej klasie. – stenix

1

Niewielka różnica w stosunku do rozwiązania scaramouche. Rozwiązanie

List<JobExecutionContext> jobs = jobExecutionContext.getScheduler().getCurrentlyExecutingJobs(); 
for (JobExecutionContext job : jobs) { 
    if (job.getTrigger().equals(jobExecutionContext.getTrigger()) && !job.getFireInstanceId().equals(jobExecutionContext.getFireInstanceId()) { 
     logger.info("There's another instance running, so leaving" + this); 
     return; 
    } 

} 

Scaramouche nie powiedzie się, gdy nie jest pojedynczy przypadek stosowany do wszystkich JobExecutions (powrót singleton przy użyciu niestandardowej klasy JobFactory zamiast nazywając newInstance() dla każdego wykonania)

1

Jeśli używasz org.springframework.scheduling.quartz.QuartzJobBean:

protected void executeInternal(JobExecutionContext context) throws JobExecutionException { 
    try { 
     Scheduler scheduler = context.getScheduler(); 
     List<JobExecutionContext> jobs = scheduler.getCurrentlyExecutingJobs(); 
     for (JobExecutionContext job : jobs) { 
      if (job.getTrigger().equals(context.getTrigger()) && job.getJobDetail() != context.getJobDetail()) { 
       LOG.warn("Ignored!"); 
       return; 
      } 
     } 
     ... 
    } catch (SchedulerException e) { 
     LOG.error("What a luck! :'(", e); 
    } 
    ... 
} 
Powiązane problemy