J'ai un fichier WAR déployé sur le serveur Tomcat, l'une des classes sera appelée au démarrage, puis la méthode init () programmera un minuteur pour se déclencher toutes les 5 heures pour effectuer certaines tâches.
Mon code init () ressemble à ceci:
public void init()
{
TimerTask parserTimerTask = new TimerTask() {
@Override
public void run() {
XmlParser.parsePage();
}
};
Timer parserTimer = new Timer();
parserTimer.scheduleAtFixedRate(parserTimerTask, 0, PERIOD);
}
Mon application fonctionne sans problème, mais lorsque j'arrête le Tomcat en utilisant /etc/init.d/Tomcat7 stop , alors je vérifie le journal (catalina.out ) il a une entrée comme celle-ci:
GRAVE: l'application Web [/ MyApplication] semble avoir démarré un thread nommé [Timer-0] mais ne l'a pas arrêtée. Ceci est très susceptible de créer une fuite de mémoire.
Je comprends que cela est dû au fait que je programme le minuteur, mais ma question est:
setDeamon
sur true, alors le minuteur ne devrait-il pas empêcher Tomcat de s'éteindre, plutôt que de le laisser fonctionner?Merci!
[~ # ~] mise à jour [~ # ~]
J'ai changé mon code pour le suivant en fonction d'une recherche et de la réponse de DaveHowes.
Timer parserTimer;
TimerTask parserTimerTask;
public void init()
{
parserTimerTask = new TimerTask() {
@Override
public void run() {
XmlParser.parsePage();
}
};
parserTimer = new Timer();
parserTimer.scheduleAtFixedRate(parserTimerTask, 0, PERIOD);
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
Logger logger = Logger.getRootLogger();
logger.info("DETECT Tomcat SERVER IS GOING TO SHUT DOWN");
logger.info("CANCEL TIMER TASK AND TIMER");
otsParserTimerTask.cancel();
otsParserTimer.cancel();
logger.info("CANCELING COMPLETE");
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
}
Maintenant ma nouvelle question:
Merci!
[~ # ~] mise à jour [~ # ~]
Ça ne marche pas. J'ai mis une déclaration de journalisation dans la méthode contextDestroyed (), après avoir arrêté Tomcat, le fichier journal ne contient que les éléments suivants:
PowderGodAppWebService -> [7 février 2012 04:09:46 PM] INFO (PowderGodAppWebService.Java:45) :: DÉTECTER LE SERVEUR Tomcat IS VA ARRÊTER PowderGodAppWebService -> [7 février 2012 04: 09:46 PM] INFO (PowderGodAppWebService.Java:46) :: ANNULER TÂCHE ET MINUTERIE
ANNULATION TERMINEE n'est pas là.
J'ai également vérifié les processus en cours d'exécution (je ne suis pas un expert Linux, j'utilise donc simplement le moniteur d'activité de Mac.
[~ # ~] fixe [~ # ~]
J'ai changé mon code en parserTimer = new Timer(true);
pour que ma minuterie fonctionne comme un thread démon car la contextDestroyed()
est appelée après l'arrêt de Tomcat.
"Tous les servlets et filtres auront été détruits avant que les ServletContextListeners ne soient informés de la destruction du contexte."
http://docs.Oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html
Ne pas utiliser Timer
dans un environnement Java EE! Si la tâche lève une exception d'exécution, puis tout Timer
est tué et ne fonctionnera plus. Vous devez essentiellement redémarrer tout le serveur pour le remettre en marche. De plus, il est sensible aux changements d'horloge système.
Utilisez plutôt ScheduledExecutorService
. Il n'est pas sensible aux exceptions lancées dans les tâches ni aux changements d'horloge système. Vous pouvez l'arrêter par sa méthode shutdownNow()
.
Voici un exemple de ce à quoi l'ensemble de l'implémentation ServletContextListener
peut ressembler (remarque: pas d'enregistrement dans web.xml
requis grâce au nouveau @WebListener
annotation):
@WebListener
public class BackgroundJobManager implements ServletContextListener {
private ScheduledExecutorService scheduler;
@Override
public void contextInitialized(ServletContextEvent event) {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new YourParsingJob(), 0, 5, TimeUnit.HOUR);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
scheduler.shutdownNow();
}
}
La méthode de destruction des servlets est appelée alors que la servlet est sur le point d'être déchargée. Vous pouvez annuler le minuteur à partir de là, à condition que vous modifiiez la portée du parserTimer lui-même pour en faire une variable d'instance. Je ne vois pas de problème avec cela à condition que vous y accédiez uniquement depuis init et destroy.
Le moteur de servlet est libre de décharger le servlet chaque fois qu'il le juge opportun, mais en pratique, je ne l'ai jamais vu appelé lorsque le moteur de servlet est arrêté - d'autres personnes peuvent avoir d'autres expériences de cela, bien que cela puisse me prouver le contraire.