web-dev-qa-db-fra.com

Comment exécuter une tâche en arrière-plan dans une application Web basée sur un servlet?

J'utilise Java et je veux garder un servlet en fonctionnement continu dans mon application, mais je ne sais pas comment le faire. Mon servlet a une méthode qui donne le nombre d'utilisateurs de une base de données sur une base quotidienne ainsi que le nombre total d'utilisateurs de toute la base de données. Je veux donc que le servlet fonctionne en permanence pour cela.

89
pritsag

Votre problème est que vous comprenez mal le but de la servlet . Il est destiné à agir sur les requêtes HTTP, rien de plus. Vous voulez juste une tâche en arrière-plan qui s'exécute une fois par jour.

EJB disponible? Utilisation @Schedule

Si votre environnement prend en charge EJB (c'est-à-dire un vrai Java EE tel que WildFly, JBoss, TomEE, Payara, GlassFish, etc.), utilisez alors @Schedule à la place. Voici quelques exemples:

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

} 

Oui, c'est vraiment tout. Le conteneur le ramasse et le gère automatiquement.

EJB indisponible? Utilisez ScheduledExecutorService

Si votre environnement ne prend pas en charge les EJB (c'est-à-dire que vous n'utilisez pas un vrai Java EE, mais un conteneur de servlet barebones tel que Tomcat, Jetty, etc.), utilisez alors ScheduledExecutorService . Cela peut être initié par un ServletContextListener . Voici un exemple de lancement:

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

}

À quoi ressemblent les catégories d'emplois:

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

}

Ne pensez jamais à utiliser Java.util.Timer/Java.lang.Thread dans un Java environnement basé sur EE/Servlet

Enfin et surtout, jamais utilisez directement Java.util.Timer et/ou Java.lang.Thread in Java EE. C'est une recette pour des problèmes. Une explication détaillée peut être trouvée dans cette réponse relative à JSF sur la même question: Spawning threads in a JSF managed bean for tâches planifiées à l'aide d'une minuterie .

201
BalusC

Je suggérerais d'utiliser une bibliothèque comme quartz pour exécuter la tâche à intervalles réguliers. Que fait vraiment le servlet? Il vous envoie un rapport?

4
Twister

Implémentez deux classes et appelez startTask() dans 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
Ali Alimohammadi
2
Rookie

Dans un système de production qui peut avoir plusieurs conteneurs non jee en marche. Utilisez un planificateur d'entreprise comme le planificateur Quartz qui peut être configuré pour utiliser une base de données pour la tâche maamgememt.

0
Jeryl Cook