Come eseguire un'attività in background in un'applicazione web basata su servlet?


97

Sto usando Java e voglio mantenere un servlet continuamente in esecuzione nella mia applicazione, ma non so come farlo. Il mio servlet ha un metodo che fornisce il conteggio degli utenti da un database su base giornaliera e il conteggio totale degli utenti dall'intero database. Quindi voglio mantenere il servlet continuamente in esecuzione per questo.


Cosa intendi con "continuamente in esecuzione"?
skaffman

1
cosa intendi per corsa continua?
Funzionerà

2
Non capisco perché deve funzionare continuamente ... se qualcuno vuole il 'conteggio utenti' allora chiamano il tuo metodo servlet e glielo dai?
trojanfoe

@trojanfoe In realtà voglio il conteggio utenti su base giornaliera, quindi per questo dovrò eseguire il servlet manualmente tutti i giorni, quindi invece di farlo voglio eseguire il servlet continuamente. quindi non avrò bisogno di eseguire il servlet tutti i giorni.
pritsag

1
@pritsag: un servlet è lì per servire le richieste degli utenti, non per eseguire lavori batch.
skaffman

Risposte:


217

Il tuo problema è che fraintendi lo scopo del servlet . È destinato ad agire su richieste HTTP, niente di più. Vuoi solo un'attività in background che viene eseguita una volta su base giornaliera.

EJB disponibile? Uso@Schedule

Se il tuo ambiente supporta EJB (cioè un vero server Java EE come WildFly, JBoss, TomEE, Payara, GlassFish, ecc.), Usa @Scheduleinvece. Ecco alcuni esempi:

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

} 

Sì, è davvero tutto. Il container lo ritirerà e lo gestirà automaticamente.

EJB non disponibile? UsoScheduledExecutorService

Se il tuo ambiente non supporta EJB (ovvero non stai utilizzando non un vero server Java EE, ma un servletcontainer barebone come Tomcat, Jetty, ecc.), Allora usa ScheduledExecutorService. Questo può essere avviato da a ServletContextListener. Ecco un esempio di kickoff:

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

}

Dove le classi di lavoro hanno questo aspetto:

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

}

Non pensare mai di utilizzare java.util.Timer/ java.lang.Threadin un ambiente basato su Java EE / Servlet

Ultimo ma non meno importante, non utilizzare mai direttamente java.util.Timere / o java.lang.Threadin Java EE. Questa è la ricetta per i guai. Una spiegazione elaborata può essere trovata in questa risposta correlata a JSF alla stessa domanda: generazione di thread in un bean gestito JSF per attività pianificate utilizzando un timer .


9
@BalucS Grazie signore, la tua soluzione mi ha aiutato e ho imparato a conoscere ScheduledExecutorService che era nuovo per me perché sono nuovo in java. Grazie ancora.
pritsag

@BalusC: Dove dovrebbe essere inserita la classe UpdateCounts in web.xml?
Ashwin

1
@Ashwin web.xml è un descrittore di distribuzione . La classe UpdateCount non è correlata alla distribuzione, quindi non deve essere inserita in web.xml
informatik01

12
Un problema cruciale con a ScheduledExecutorService: assicurati di catturare tutte le eccezioni nel tuo esecutore. Se un'eccezione sfugge dal tuo runmetodo, l'esecutore interrompe silenziosamente l'esecuzione. Questa è una caratteristica, non un bug. Leggi il documento e studia con un po 'di google.
Basil Bourque

1
@Agi: ciò accadrà se scheduler.shutdownNow()non viene richiamato correttamente come nell'esempio. Se non viene richiamato, il thread di pianificazione continuerà effettivamente a funzionare.
BalusC

4

Suggerirei di utilizzare una libreria come quartz per eseguire l'attività a intervalli regolari. Cosa fa veramente il servlet? Ti manda una segnalazione?


sì, mi dà il conteggio degli utenti creati al giorno e anche il conteggio degli utenti totali nel mio database.
pritsag

1
euu? Puoi descrivere l'architettura COMPLETA del tuo sistema. Mi sono perso.
Twister

@Twister sono nuovo in Java e in fase di apprendimento signore e davvero non conosco molto i servlet.
pritsag

Il problema non riguarda il servlet. Qual è l'applicazione di cui parli? (ps: è una cattiva idea cancellare i tuoi commenti, soprattutto i commenti a cui ho risposto)
Twister

@twister quando l'utente accederà all'applicazione, riceverà tutti i dettagli come quanti utenti vengono creati oggi, quanti utenti sono desiderati fino ad ora ecc. e voglio eseguire il servlet in background continuamente in modo che l'utente possa ottenere gli aggiornamenti So che questa non è la spiegazione corretta. (Ps: so che è stata una cattiva idea, scusami)
pritsag

3

Implementare due classi e chiamare startTask()in 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();
    }
}

3
Questo NON è sicuramente il modo per farlo in un'applicazione web - guarda invece la risposta sopra di @BalusC - ha ragione qui e direi che puoi fidarti di tutte le sue risposte.
Yoshiya


0

In un sistema di produzione che può avere più contenitori non jee in esecuzione. Utilizzare uno scheduler aziendale non come lo scheduler Quartz che può essere configurato per utilizzare un database per le attività di gestione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.