Jak uruchomić zadanie w tle w aplikacji internetowej opartej na serwletach?

97

Używam języka Java i chcę, aby serwlet działał nieprzerwanie w mojej aplikacji, ale nie rozumiem, jak to zrobić. Mój serwlet ma metodę, która codziennie podaje liczbę użytkowników z bazy danych, a także całkowitą liczbę użytkowników z całej bazy danych. Dlatego chcę, aby serwlet działał nieprzerwanie.

pritsag
źródło
Co masz na myśli, mówiąc „ciągłe działanie”?
skaffman
1
co masz na myśli mówiąc o ciągłym biegu? Będzie działać tak długo, jak będzie działać serwer aplikacji
fmucar,
2
Nie rozumiem, dlaczego musi działać w sposób ciągły… jeśli ktoś chce „zliczać użytkowników”, to wywołuje metodę serwletu i dajesz mu ją?
trojanfoe
@trojanfoe Właściwie chcę codziennie mieć liczbę użytkowników, więc w tym celu będę musiał codziennie uruchamiać serwlet ręcznie, więc zamiast robić to, chcę uruchamiać serwlet w sposób ciągły. Więc nie będę musiał uruchamiać serwletu codziennie.
pritsag
1
@pritsag: Aplet służy do obsługi żądań użytkowników, a nie do uruchamiania zadań wsadowych.
skaffman

Odpowiedzi:

217

Twój problem polega na tym, że nie rozumiesz celu serwletu . Ma działać na żądaniach HTTP, nic więcej. Potrzebujesz tylko zadania w tle, które jest uruchamiane raz dziennie.

EJB jest dostępny? Posługiwać się@Schedule

Jeśli twoje środowisko obsługuje EJB (tj. Prawdziwy serwer Java EE, taki jak WildFly, JBoss, TomEE, Payara, GlassFish itp.), Użyj @Schedulezamiast tego. Oto kilka przykładów:

@Singleton
public class BackgroundJobManager {

    @Schedule(hour="0", minute="0", second="0", persistent=false)
    public void someDailyJob() {
        // Do your job here which should run every start of day.
    }

    @Schedule(hour="*/1", minute="0", second="0", persistent=false)
    public void someHourlyJob() {
        // Do your job here which should run every hour of day.
    }

    @Schedule(hour="*", minute="*/15", second="0", persistent=false)
    public void someQuarterlyJob() {
        // Do your job here which should run every 15 minute of hour.
    }

    @Schedule(hour="*", minute="*", second="*/5", persistent=false)
    public void someFiveSecondelyJob() {
        // Do your job here which should run every 5 seconds.
    }

} 

Tak, to naprawdę wszystko. Kontener automatycznie odbierze go i nim zarządza.

EJB niedostępny? Posługiwać sięScheduledExecutorService

Jeśli twoje środowisko nie obsługuje EJB (tj. Nie używasz nie prawdziwego serwera Java EE, ale serwletów szkieletowych, takich jak Tomcat, Jetty itp.), Użyj ScheduledExecutorService. Może to zostać zainicjowane przez ServletContextListener. Oto przykład wstępny:

@WebListener
public class BackgroundJobManager implements ServletContextListener {

    private ScheduledExecutorService scheduler;

    @Override
    public void contextInitialized(ServletContextEvent event) {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS);
        scheduler.scheduleAtFixedRate(new SomeHourlyJob(), 0, 1, TimeUnit.HOURS);
        scheduler.scheduleAtFixedRate(new SomeQuarterlyJob(), 0, 15, TimeUnit.MINUTES);
        scheduler.scheduleAtFixedRate(new SomeFiveSecondelyJob(), 0, 5, TimeUnit.SECONDS);
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        scheduler.shutdownNow();
    }

}

Gdzie zajęcia wyglądają tak:

public class SomeDailyJob implements Runnable {

    @Override
    public void run() {
        // Do your daily job here.
    }

}
public class SomeHourlyJob implements Runnable {

    @Override
    public void run() {
        // Do your hourly job here.
    }

}
public class SomeQuarterlyJob implements Runnable {

    @Override
    public void run() {
        // Do your quarterly job here.
    }

}
public class SomeFiveSecondelyJob implements Runnable {

    @Override
    public void run() {
        // Do your quarterly job here.
    }

}

Nigdy nie myśl o używaniu java.util.Timer/ java.lang.Threadw środowisku opartym na Java EE / Servlet

Wreszcie, nigdy nie używaj bezpośrednio java.util.Timeri / lub java.lang.Threadw Java EE. To recepta na kłopoty. Szczegółowe wyjaśnienie można znaleźć w tej związanej z JSF odpowiedzi na to samo pytanie: Odradzanie wątków w komponencie bean zarządzanym przez JSF dla zaplanowanych zadań przy użyciu timera .

BalusC
źródło
9
@BalucS Dziękuję, panie, twoje rozwiązanie pomogło mi i dowiedziałem się o usłudze ScheduledExecutorService, która była dla mnie nowa jako nowy w java. Jeszcze raz dziękuję.
pritsag
@BalusC: Gdzie należy umieścić klasę UpdateCounts w web.xml?
Ashwin,
1
@Ashwin web.xml to deskryptor wdrażania . Klasa UpdateCount nie jest związana z wdrożeniem, więc nie trzeba jej umieszczać w web.xml
informatik01
12
Jeden kluczowy problem z ScheduledExecutorService: Upewnij się, że przechwyciłeś wszystkie wyjątki w swoim executorze. Jeśli wyjątek ucieka z runmetody, moduł wykonawczy po cichu przestaje wykonywać. To jest funkcja, a nie błąd. Przeczytaj dokument i naucz się googlować.
Basil Bourque
1
@Agi: stanie się tak, jeśli scheduler.shutdownNow()nie zostanie poprawnie wywołany, jak na przykładzie. Jeśli to nie zostanie wywołane, wątek harmonogramu będzie rzeczywiście działał.
BalusC
4

Sugerowałbym użycie biblioteki takiej jak kwarc, aby uruchamiać zadanie w regularnych odstępach czasu. Co tak naprawdę robi serwlet? Wysyła raport?

Tornado
źródło
tak, daje mi liczbę utworzonych użytkowników dziennie, a także całkowitą liczbę użytkowników w mojej bazie danych.
pritsag
1
huuu? Czy możesz opisać PEŁNĄ architekturę swojego systemu. Zgubiłem się.
Twister
@Twister jestem nowy w Javie i na etapie uczenia się sir i naprawdę nie wiem zbyt wiele o serwletach.
pritsag
Problem nie dotyczy serwletu. O jakiej aplikacji mówisz? (ps: jest to zły pomysł, aby usuwać swoich uwag, zwłaszcza komentuje Odpowiedziałem do)
Twister
@twister, gdy użytkownik trafi do aplikacji, otrzyma wszystkie szczegóły, takie jak liczba utworzonych użytkowników dzisiaj, ilu użytkowników jest pożądanych do tej pory itp. i chcę, aby serwlet działał w tle w sposób ciągły, aby użytkownik mógł otrzymywać aktualizacje Wiem, że to nie jest właściwe wyjaśnienie. (Ps: wiem, że to był zły pomysł. Przepraszam za to.)
pritsag
3

Wdrożenie dwie klasy i wywołać startTask()w main.

public void startTask()
{
    // Create a Runnable
    Runnable task = new Runnable() {
        public void run() {
            while (true) {
                runTask();
            }
        }
    };

    // Run the task in a background thread
    Thread backgroundThread = new Thread(task);
    // Terminate the running thread if the application exits
    backgroundThread.setDaemon(true);
    // Start the thread
    backgroundThread.start();
}

public void runTask()
{
    try {
        // do something...         
        Thread.sleep(1000);

    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
Ali Alimohammadi
źródło
3
To zdecydowanie NIE jest sposób na zrobienie tego w aplikacji internetowej - zamiast tego spójrz na powyższą odpowiedź @BalusC - ma on tutaj rację i powiedziałbym, że możesz ufać wszystkim jego odpowiedziom.
Yoshiya
0

W systemie produkcyjnym, który może mieć uruchomionych wiele kontenerów innych niż jee. Użyj innego programu planującego w przedsiębiorstwie, takiego jak harmonogram kwarcowy, który można skonfigurować tak, aby używał bazy danych do zadań maamgememt.

Jeryl Cook
źródło