Alarmes JDring - Rappels des limites et bonnes pratiques
Voici un petite petite piqûre de rappel sur le fonctionnement des Alarmes JDring, de l'impact d'un mauvais usage et des bonnes pratiques à suivre.
Ce billet s'adresse tous les consultants et autres intervenants techniques autour de JCMS.
Rappel du fonctionnement
En préambule un rappel rapide du fonctionnement de JDring.
Consultez l'article Programmation d'alarmes et de tâches planifiées avec l'API JDring pour plus d'informations détaillées à ce sujet.
Pour planifier une tâches, 2 élements principaux entrent en jeux :
- Coté ordonnanceur : Un AlarmManager
C'est l'objet qui est responsable de la planification des tâches.
A chaque instance d'AlarmManager correspond un thread (deamon) qui tourne en tache de fond et qui attends l'exécution de la prochaine alarme. - Coté alarme : Un AlarmEntry et son AlarmListener
C'est une instance d'AlarmEntry qui définit la planification de l'alarme,
C'est une instance d'AlarmListener qui se charge de son exécution.
On ajoute une alarme dans un AlarmManager en faisant alarmManager.addAlarm(entry);
Dans JCMS, on peut :
- récupérer le CommonAlarmManager fourre-tout via
channel.getCommonAlarmManager() - récupérer un AlarmManager existant (ou le crééer si il n'existe pas) en invoquant
channel.getAlarmManager(String nomDuManager)
Limites et Bonnes pratiques
Il y a quelques limites qu'il faut impérativement comprendre pour bien utiliser les alarmes :
1. Un AlarmManager exécute une seule alarme à la fois
Si une alarme A1 (rattaché à un AlarmManager AM1), est en cours d'exécution, toutes les autres alarmes du même AlarmManager AM1 devront patienter que A1 soit fini avant de pouvoir être invoqué.
Conséquence : Une alarme peut décaler dans le temps l'exécution des autres alarmes d'un même AlarmManager
Bonne pratique #1 : Il ne faut pas que trop d'alarmes différentes soit rattachées à un seul et meme AlarmManager sous peine de perturber leur fonctionnement.
Notamment, dans JCMS, il y a un "CommonAlarmManager", c'est un alarmManager fourre-tout, qu'on doit utiliser uniquement pour toute les alarmes "mineures" qui ne prennent pas de temps. Sous risque de décaler les autres alarmes de ce manager.
2. L'ajout d'une alarme est bloquant si une alarme est en cours d'exécution
Si une alarme a1 quelconque est en cours d'exécution par l'AlarmManager AM1, alors tout invocation de AM1.addAlarm(a2) dans un autre thread sera bloquée tant que l'exécution d'alarme a1 n'est pas terminée.
Conséquence : Lors de l'ajout d'une nouvelle alarme dans un AlarmManager existant, il est possible que vous soyez bloqué si cet AlarmManager est déjà occupé. Et inversement si votre alarme est trop longue, vous pourriez bloquer l'ajout d'une alarme
Cette limite est à l'origine du bug JCMS-2891, dans lequel le démarrage de JCMS se retrouve bloqué pendant un certain temps car une alarme en cours d'exécution dans le CommonAlarmManager empêche l'ajout d'une autre alarme dans la suite du démarrage.
Bonne pratique #2 :
- Pour ne pas être bloqué lors de l'ajout : choissisez bien votre AlarmManager,
- Pour ne pas bloquer les autres : Lors du démarrage de JCMS, ne planifiez pas immédiatement dans le "CommonAlarmManager" des alarmes dont l'exécution pourrait prendre du temps. Sans cela votre alarme pourrait s'exécuter alors que le démarrage est toujours en cours dans le thread principal, et si une autre alarme doit être planifié elle devra vous attendre!
3. Un AlarmManager == Un thread
La création d'un AlarmManager créé immédiatement un nouveau Thread Java.
Conséquence : Un AlarmManager dédié c'est bien pour gérer une alarme de façon totalement isolée, sans être impacté pas d'autre alarmes et sans impacter par les autres, mais c'est aussi coûteux car cela occupe un Thread Java pour cela.
Bonne pratique #3 : Créez des nouveau AlarmManager avec parcimonie.