Wie führe ich eine Hintergrundaufgabe in einer Servlet-basierten Webanwendung aus?

97

Ich verwende Java und möchte ein Servlet in meiner Anwendung kontinuierlich ausführen, aber ich verstehe nicht, wie es geht. Mein Servlet verfügt über eine Methode, die täglich die Anzahl der Benutzer aus einer Datenbank sowie die Gesamtzahl der Benutzer aus der gesamten Datenbank angibt. Deshalb möchte ich das Servlet dafür kontinuierlich laufen lassen.

Pritsag
quelle
Was meinst du mit "ununterbrochen laufen"?
Skaffman
1
Was meinst du mit kontinuierlichem Laufen? Es wird so lange ausgeführt, wie Ihr App-Server ausgeführt wird
fmucar
2
Ich verstehe nicht, warum es ununterbrochen laufen muss ... Wenn jemand die 'Benutzeranzahl' will, ruft er Ihre Servlet-Methode auf und Sie geben sie ihm?
Trojaner
@trojanfoe Eigentlich möchte ich die Anzahl der Benutzer täglich, daher muss ich das Servlet jeden Tag manuell ausführen. Stattdessen möchte ich das Servlet kontinuierlich ausführen. Daher muss ich das Servlet nicht jeden Tag ausführen.
Pritsag
1
@pritsag: Ein Servlet dient dazu, Benutzeranforderungen zu bearbeiten und keine Stapeljobs auszuführen.
Skaffman

Antworten:

217

Ihr Problem ist, dass Sie den Zweck des Servlets falsch verstehen . Es ist beabsichtigt, auf HTTP-Anfragen zu reagieren, nicht mehr. Sie möchten nur eine Hintergrundaufgabe, die einmal täglich ausgeführt wird.

EJB verfügbar? Verwenden@Schedule

Wenn Ihre Umgebung EJB unterstützt (dh einen echten Java EE-Server wie WildFly, JBoss, TomEE, Payara, GlassFish usw.), verwenden Sie @Schedulestattdessen. Hier sind einige Beispiele:

@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.
    }

} 

Ja, das ist wirklich alles. Der Container nimmt ihn automatisch auf und verwaltet ihn.

EJB nicht verfügbar? VerwendenScheduledExecutorService

Wenn Ihre Umgebung EJB nicht unterstützt (dh Sie verwenden keinen echten Java EE-Server, sondern einen Barebone-Servlet-Container wie Tomcat, Jetty usw.), verwenden Sie ScheduledExecutorService. Dies kann durch a eingeleitet werden ServletContextListener. Hier ist ein Kickoff-Beispiel:

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

}

Wo die Jobklassen so aussehen:

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

}

Denken Sie niemals daran, java.util.Timer/ java.lang.Threadin einer Java EE / Servlet-basierten Umgebung zu verwenden

Last but not least, niemals direkt java.util.Timerund / oder java.lang.Threadin Java EE verwenden. Dies ist ein Rezept für Ärger. Eine ausführliche Erklärung finden Sie in dieser JSF-bezogenen Antwort auf dieselbe Frage: Laichen von Threads in einer von JSF verwalteten Bean für geplante Aufgaben mithilfe eines Timers .

BalusC
quelle
9
@BalucS Vielen Dank, Sir, Ihre Lösung hat mir geholfen und ich habe von ScheduledExecutorService erfahren, der für mich neu war, da ich neu bei Java bin. Nochmals vielen Dank.
Pritsag
@BalusC: Wo soll die Klasse UpdateCounts in web.xml abgelegt werden?
Ashwin
1
@Ashwin web.xml ist ein Deployment Descriptor . Die Klasse UpdateCount ist nicht mit der Bereitstellung verbunden, daher muss sie nicht in web.xml
informatik01
12
Ein entscheidendes Problem bei a ScheduledExecutorService: Achten Sie darauf, alle Ausnahmen in Ihrem Executor zu erfassen . Wenn eine Ausnahme von Ihrer runMethode ausgeht, beendet der Executor die Ausführung stillschweigend. Dies ist eine Funktion, kein Fehler. Lesen Sie das Dokument und lernen Sie etwas googeln.
Basil Bourque
1
@Agi: Dies geschieht, wenn scheduler.shutdownNow()gemäß Beispiel nicht korrekt aufgerufen wird. Wenn dies nicht aufgerufen wird, wird der Zeitplan-Thread tatsächlich weiter ausgeführt.
BalusC
4

Ich würde vorschlagen, eine Bibliothek wie Quarz zu verwenden, um die Aufgabe in regelmäßigen Abständen auszuführen. Was macht das Servlet wirklich? Es sendet Ihnen einen Bericht?

Twister
quelle
Ja, es gibt mir die Anzahl der Benutzer, die pro Tag erstellt wurden, und auch die Anzahl der Benutzer in meiner Datenbank.
Pritsag
1
huuu? Können Sie die vollständige Architektur Ihres Systems beschreiben? Ich bin verloren.
Twister
@ Twister Ich bin neu in Java und in der Lernphase, Sir, und ich weiß wirklich nicht viel über die Servlets.
Pritsag
Das Problem betrifft nicht das Servlet. Über welche Anwendung sprechen Sie? (ps: es ist eine schlechte Idee, Ihre Kommentare zu löschen, insbesondere Kommentare, auf die ich geantwortet habe)
Twister
@twister Wenn der Benutzer auf die Anwendung trifft, erhält er alle Details, wie viele Benutzer heute erstellt werden, wie viele Benutzer bis jetzt beschäftigt sind usw. und ich möchte das Servlet kontinuierlich im Hintergrund ausführen, damit der Benutzer die Updates erhalten kann Ich weiß, dass dies nicht die richtige Erklärung ist. (Ps: Ich weiß, dass es eine schlechte Idee war.
Tut mir
3

Implementieren Sie zwei Klassen und rufen Sie startTask()an 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
quelle
3
Dies ist definitiv NICHT der Weg, dies in einer Webanwendung zu tun - sehen Sie sich stattdessen die Antwort oben von @BalusC an - er ist hier richtig und ich würde sagen, dass Sie allen seinen Antworten vertrauen können.
Yoshiya
0

In einem Produktionssystem, in dem möglicherweise mehrere Nicht-Jee-Container ausgeführt werden. Verwenden Sie einen anderen Enterprise-Scheduler wie den Quartz-Scheduler, der so konfiguriert werden kann, dass eine Datenbank für die Aufgabenverwaltung verwendet wird.

Jeryl Cook
quelle