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

97

J'utilise Java et je souhaite maintenir un servlet en cours d'exécution dans mon application, mais je ne sais pas comment le faire. Mon servlet a une méthode qui donne le nombre d'utilisateurs à partir d'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 continue de fonctionner pour cela.

pritsag
la source
Que voulez-vous dire par «fonctionnement continu»?
skaffman
1
qu'entendez-vous par courir continuellement? Il fonctionnera tant que votre serveur d'applications fonctionnera
fmucar
2
Je ne comprends pas pourquoi il doit fonctionner en continu ... si quelqu'un veut le «nombre d'utilisateurs», il appelle votre méthode servlet et vous le leur donnez?
trojanfoe
@trojanfoe En fait, je veux le nombre d'utilisateurs sur une base quotidienne, donc pour cela, je devrai exécuter le servlet manuellement tous les jours, donc au lieu de le faire, je veux exécuter le servlet de manière continue, donc je n'ai pas besoin d'exécuter le servlet tous les jours.
pritsag le
1
@pritsag: Un servlet est là pour répondre aux demandes des utilisateurs, pas pour exécuter des travaux par lots.
skaffman le

Réponses:

217

Votre problème est que vous ne comprenez pas le but du servlet . Il est destiné à agir sur les requêtes HTTP, rien de plus. Vous voulez juste une tâche d'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 serveur Java EE tel que WildFly, JBoss, TomEE, Payara, GlassFish, etc.), utilisez à la @Scheduleplace. 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 récupérera et le gérera automatiquement.

EJB indisponible? UtilisationScheduledExecutorService

Si votre environnement ne prend pas en charge EJB (c'est-à-dire que vous n'utilisez pas un vrai serveur Java EE, mais un conteneur de servlet simple tel que Tomcat, Jetty, etc.), utilisez 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();
    }

}

Où les classes d'emplois ressemblent à ceci:

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.Threaddans un environnement basé sur Java EE / Servlet

Last but not least, n'utilisez jamais directement java.util.Timeret / ou java.lang.Threaddans Java EE. C'est la recette des ennuis. Une explication détaillée peut être trouvée dans cette réponse relative à JSF sur la même question: Générer des threads dans un bean géré JSF pour les tâches planifiées à l'aide d'un minuteur .

BalusC
la source
9
@BalucS Merci monsieur, votre solution m'a aidé et j'ai découvert ScheduledExecutorService qui était nouveau pour moi car je suis nouveau sur java.Merci encore une fois.
pritsag
@BalusC: Où la classe UpdateCounts doit-elle être placée dans web.xml?
Ashwin
1
@Ashwin web.xml est un descripteur de déploiement . La classe UpdateCount n'est pas liée au déploiement, elle n'a donc pas besoin d'être placée dans web.xml
informatik01
12
Un problème crucial avec a ScheduledExecutorService: Assurez-vous de capturer toutes les exceptions dans votre exécuteur testamentaire. Si une exception s'échappe de votre runméthode, l'exécuteur s'arrête silencieusement de s'exécuter. Ceci est une fonctionnalité et non un bug. Lisez le document et étudiez avec googler.
Basil Bourque
1
@Agi: cela se produira si scheduler.shutdownNow()n'est pas correctement invoqué selon l'exemple. Si cela n'est pas appelé, le thread de planification continuera effectivement à fonctionner.
BalusC
4

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

Tornade
la source
oui, cela me donne le décompte des utilisateurs créés par jour et aussi le décompte du nombre total d'utilisateurs dans ma base de données.
pritsag le
1
huuu? Pouvez-vous décrire l'architecture COMPLÈTE de votre système. Je suis perdu.
Twister le
@Twister je suis nouveau sur java et en phase d'apprentissage monsieur et je ne sais vraiment pas grand chose sur les servlets.
pritsag le
Le problème ne concerne pas le servlet. De quelle application parlez-vous? (ps: c'est une mauvaise idée de supprimer vos commentaires, en particulier les commentaires auxquels j'ai répondu)
Twister
@twister lorsque l'utilisateur appuiera sur l'application, il obtiendra tous les détails tels que le nombre d'utilisateurs créés aujourd'hui, le nombre d'utilisateurs qui sont intéressés jusqu'à présent, etc. et je veux exécuter le servlet en arrière-plan de manière continue afin que l'utilisateur puisse obtenir les mises à jour .Je sais que ce n'est pas la bonne explication. (Ps: je sais que c'était une mauvaise idée.solé pour ça.)
pritsag
3

Mettre en œuvre deux classes et appeler startTask()à 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
la source
3
Ce n'est certainement PAS la façon de le faire dans une application Web - regardez plutôt la réponse ci-dessus de @BalusC - il a raison ici et je dirais que vous pouvez faire confiance à toutes ses réponses.
Yoshiya
0

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

Jeryl Cook
la source