Gestion des ressources JMS de WebLogic en JMX avec Groovy - 1ère partie Monitoring

Les MBeans JMSDestinationRuntimeMBean

WebLogic expose en JMX toutes les apis nécessaires à la mise en oeuvre d’un monitoring et d’un management complet de ces ressources JMS.
La plupart correspondent à des fonctionnalités déjà proposées par la console WebLogic (accessibles via l’onglet Monitoring des destinations dans les modules jms)

Cela passe principalement par le MBean JMSDestinationRuntimeMBean. WebLogic expose une instance de ce MBean pour chaque Queue et Topic physique (cad que pour une queue distribuée, il y aura autant de Mbean que de serveur jms sur lequel est déployée la queue distribuée)

Cet MBean est décrit comme tous les autres ici : http://download.oracle.com/docs/cd/E13222_01/wls/docs100/wlsmbeanref/core/index.html
mais cet doc manque parfois de précision sur les paramètres des opérations les moins usuelles. Un des buts de ce post est de montrer comment les utiliser.

Comme lors de mon post sur Groovy et JMX, j’utiliserais le langage Groovy pour faciliter la manipulation de cet MBean
On notera toutefois que les améliorations que j’avais apportées en étendant GroovyMBean pour créer GroovierMBean, ont surtout de l’intérêt pour parcourir les MBeans de configuration de WebLogic. Dans le cadre de ce post, les capacités de GroovyMBean suffiseront amplement
(Pour plus de facilité j’utiliserais tout de même une version simplifiée de la classe WebLoMBean introduite dans le précédent post)

Les scripts décrits ici ont été mises en oeuvre sur un Weblogic 10.x. Ils fonctionneront aussi sur un WebLogic 9.x. (Par contre, la classe WebLoMBean n’est pas compatible avec WebLo 8.x, les serveurs JMX n’étant pas accessibles de la même façon dans cette version). Vous trouverez joint un zip contenant les scripts décrits plus bas ainsi que les infos nécessaires pour les exécuter.

Avant tout : accéder aux instances de JMSDestinationRuntimeMBean

Le serveur JMX du domaine exposé par le serveur d’admin donne accés aux mbeans déployés sur tous les noeuds du domaine WebLogic.
On pourra bien sûr faire les mêmes manipulations en se connectant au serveur runtime d’un des serveurs managés d’un domaine, on se limitera alors aux ressources déployés sur ce noeud. (La classe WebLoMBean fournit avec le code joint permet l’accès à l’un ou l’autre de ces types de serveurs)

Pour obtenir une référence à un ou des JMSDestinationRuntimeMBean (plus exactement obtenir l’ObjectName représentant cet objet sur le serveur), les apis de recherche du serveur jmx seront utilisées.
Il suffit juste de savoir que ces MBeans sont définis par une Key Property “Type” valant “JMSDestinationRuntime”
Le pattern d’ObjectName “com.bea:Type=JMSDestinationRuntime,*” permet ainsi de filtrer sur les MBeans de ce type.

Ainsi le script Groovy suivant pemet de lister toutes les queues ou topics du domaine WebLogic pointé :

import javax.management.ObjectName; import fr.ippon.groovy.util.WebLoMBean; def mbs = WebLoMBean.getDomainMBeanServer('t3://localhost:7001', 'weblogic', 'weblogic') def destFilter = null def destinationsMBeanNames = mbs.queryNames(new ObjectName('com.bea:Type=JMSDestinationRuntime,*'), destFilter) println "Liste des ObjectName :" destinationsMBeanNames.each { println it } println '' println "Liste des queues et topics :" def destinationMbeans = destinationsMBeanNames.collect { new GroovyMBean(mbs, it) } destinationMbeans.each { mbean -> println "$mbean.Name" }

Dans ce script WebLoMBean.getDomainMBeanServer renvoie une instance de javax.management.MBeanServerConnection pointant sur le serveur jmx du domaine, utilisée ensuite pour rechercher les ObjectName des JMSDestinationRuntimeMBean
qui sont stockés dans la variable destinationsMBeanNames.
Plus bas, on transforme chaque ObjectName en GroovyMbean (via la méthode collect de Groovy) pour pouvoir accéder facilement aux attributs de ces MBeans (ici, uniquement l’attribut Name)

Exemple : Liste des ObjectName : com.bea:ServerRuntime=Server1,Name=MyJMSModule!MySimpleQueue,Location=Server1,Type=JMSDestinationRuntime,JMSServerRuntime=JMSServer-0 com.bea:ServerRuntime=Server1,Name=MyJMSModule!JMSServer-0@MyDistributedQueue,Location=Server1,Type=JMSDestinationRuntime,JMSServerRuntime=JMSServer-0 com.bea:ServerRuntime=Server2,Name=MyJMSModule!JMSServer-0@MyDistributedQueue,Location=Server2,Type=JMSDestinationRuntime,JMSServerRuntime=JMSServer-1 Liste des queues et topics : MyJMSModule!MySimpleQueue MyJMSModule!JMSServer-0@MyDistributedQueue MyJMSModule!JMSServer-1@MyDistributedQueue

( Rq : au niveau JMX, la clef Name des ObjectName contient une information potentiellement différente de l’attribut Name du MBean, WebLogic y stockant la même valeur, on peut utiliser celle qui nous arrange)

Filtrer sur le nom de la queue

L’attribut Name peut avoir plusieurs formes, en particulier :

  • Pour une simple queue (ou un topic) on aura :

MyJMSModule!MySimpleQueue

où MyJMSModule est le nom du module au sein duquel a été créé la queue et MySimpleQueue est le nom de la queue (attention, c’est un champ différent du nom jndi saisie dans la console WebLogic !)

  • Pour une queue (ou un topic) uniformément distribuées (UDD):

MyJMSModule!JMSServer-0@MyDistributedQueue
MyJMSModule!JMSServer-1@MyDistributedQueue

où JMSServer-x sont les noms des serveurs JMS sur lesquels sont déployés les membres de la queue distribuée

On peut filtrer facilement en utilisant un filtre JMX sur les attributs. Voici un exemple de filtre pour rechercher les mbeans de toutes les queues membres d’une queue uniformément distribuée appelée MyDistributedQueue ( ce filtre est à utiliser comme deuxième argument de l’appel à la méthode queryNames dans le script précédent)

def destFilter = javax.management.Query.with { match(attr('Name'),value('*@MyDistributedQueue')) }

(Ici, ce sont des apis JMX standard du JDK exposé par la classe javax.management.Query, mais Groovy nous permet d’avoir une syntaxe moins verbeuse)

Monitoring des queues

Les JMSDestinationRuntimeMBean offrent de nombreux d’attributs

J’en utilise principalement deux :

  • MessagesCurrentCount : le nombre de message présent dans la queue (unique les messages visibles cad non pending)
  • MessagesPendingCount : le nombre de message pending dans la queue

Un compteur supérieur à zéro sur le premier peut indiquer un problème de consommation ou montrer un dysfonctionnement caractérisé par une accumulation de messages dans une queue d’erreur
Un compteur supérieur à zéro sur le second montre les queues actuellement sollicités et éventuellement des engorgements s’ils ne retombent pas à zéro rapidement.

Lister ces queues et obtenir la valeur des compteurs s’effectuent très simplement :

import javax.management.ObjectName; import javax.management.Query; import fr.ippon.groovy.util.WebLoMBean; def mbs = WebLoMBean.getDomainMBeanServer('t3://localhost:7001', 'weblogic', 'weblogic') def destFilter = javax.management.Query.with { or( gt(attr('MessagesCurrentCount'),value(0)), gt(attr('MessagesPendingCount'),value(0)) ) } def destinationsMBeanNames = mbs.queryNames(new ObjectName('com.bea:Type=JMSDestinationRuntime,*'), destFilter) def destinationMbeans = destinationsMBeanNames.collect { new GroovyMBean(mbs, it) } destinationMbeans.each { mbean -> printf " %-60s nbMsg=%-5d pending=%-5d%n", mbean.Name, mbean.MessagesCurrentCount, mbean.MessagesPendingCount }

Exemple de sortie :

MyJMSModule!JMSServer-1@MyDistributedQueue nbMsg=0 pending=3 MyJMSModule!JMSServer-1@MyDistributedQueue_error nbMsg=5 pending=0

Tips : Compter les messages récents
Suivant votre gestion des queues, vous aurez peut-être comme moi envie d’avoir une information supplémentaire :
parmi les messages présents dans un queue, combien sont arrivés depuis x heures ou jours.
C’est en particulier nécessaire sur une queue d’erreur dans laquelle s’empile plus ou moins régulièrement des messages suite à des échecs de traitement

Obtenir cette information n’est que très légèrement plus compliquée :

//... def cal = Calendar.getInstance() cal.add(Calendar.HOUR,-24) def timestamp = cal.getTimeInMillis() destinationMbeans.each { mbean -> def selector = 'JMSTimestamp > '+ timestamp def cursor = mbean.getMessages(selector,10) def recentMsg = mbean.getCursorSize(cursor) mbean.closeCursor(cursor) printf " %-60s nbMsg=%-5d recentMsg=%-5d%n" , mbean.Name, mbean.MessagesCurrentCount, recentMsg }

On commence ici à utiliser les opérations JMX exposées par le MBean. Nous reparlerons de l’opération getMessages plus tard pour lire le contenu des messages. Ici, on l’utilise uniquement pour sélectionner l’ensemble des messages qui ont moins de 24H. Pour cela un selector JMS standard est construit pour filtrer sur le timestamp jms : la date création du message.
(Le lecteur attentif aura remarqué qu’en effectuant cela on ne filtre donc pas tout à fait sur le moment où le message est arrivée dans la queue)
L’opération getCursorSize permet ensuite d’obtenir la taille de l’échantillon ainsi sélectionné.

Remarque : n’importe quel utilisateur WebLogic a accès aux attributs du MBean, mais pour appeler les opérations, il est nécessaire d’être connecté avec un utilisateur ayant plus de droit tel qu’un administrateur.

Notre petit tour de JMSDestinationRuntimeMBean s’arrête ici pour aujourd’hui. Vous trouverez ci-joint les scripts groovy mettant en oeuvre le code proposé dans cet article.
Dans les prochaines parties, nous verrons comment utiliser ces Mbeans pour gérer les queues (suppression, déplacement, pause) puis pour lire le contenu des messages en particulier ceux à l’état pending.

Author image
Fort de plus de 20 ans d'expérience autour de l'écosystème JavaEE, Fabien accompagne ses clients dans la conception et le cadrage de leurs projets.