mardi 6 septembre 2011

L’ordonnancement des messages ou UnitOfOrder dans JMS sous Weblogic permet de respecter l’ordre des messages en arrivées par rapport à leur ordre de production.

Il se peut que lors du passage des messages dans une architecture multitiers, la propagation de certains messages soit ralentie par rapport à d’autres, ce qui peut aboutir à une arrivée désordonnée par rapport à l’ordre de leurs créations.

Une question peut se poser si lors de la propagation des messages des événements imprévus surviennent. Quand est-il de l’ordonnancement si des mécanismes d’erreur réémettent les messages, que se passe-t-il si des messages sont perdus, l’ordonnancement est-il arrêté, etc …

Ce post restitue un test que j’ai réalisé avec un prototype mettant en œuvre le JMS UnitOfOrder avec un certain nombre de simulations d’erreur et de perturbation afin d’évaluer le fonctionnement de cette fonctionnalité.

ARCHITECTURE



Le prototype simule l’envoi de message par un client JAVA multi thread qui va simuler l’envoi de message vers la plateforme JEE.

Message texte envoyé par le producteur de message.
Threads[No Thread]-No[No message]

Deux couches sont implémentées avec une file de messages (Queue) dans lequel est déposé le message puis lues par un MDB. Celui-ci fait appel à un EJB pour remettre le message dans une autre file JMS.

En final le message est déposé dans une file de messages de sortie lue par un client JAVA qui affiche la totalité des messages produits (avec l’ordre d’arrivée).

Les traitements effectués sur les flux sont :

ü  Une temporisation aléatoire (Wait) est introduite dans chaque composant afin de désynchroniser l’ordre d’arrivée final.
ü  Une exception et levé aléatoirement sur les MDB afin de simuler les erreurs techniques. Un mécanisme de rejeux JMS est mise en place sur les files de messages pour réinjecter les messages 1 fois. Au bout du 2eme échec, le message est mis dans une file d’erreurs.
ü  Un mécanisme de switch aléatoire est placé dans l’EJB de sortie afin de simuler les exceptions applicatives.


A chaque passage dans les composants JEE, le message est enrichi en y ajoutant le nom du composant, de l’instance de passage et du temps d’attente. L’affichage final du consommateur affichant en 1er l’ordre d’arrivée

PARAMETRAGE



L’architecture mise en place est un domaine Weblogic avec deux instances en cluster et un domaine mono-instance. Les files de messages de type Queue ont été paramétrées en mode Uniform Distributed Queue sur la plate-forme cluster avec les paramétrages suivants :
                                                                                                               
  • Déclaration d’un Path Service obligatoire pour l’utilisation du UnitOfOrder en cluster ciblé sur l’instance managed1 (service singleton à migrer en cas de problème) (un seul Path Service par cluster).

${DOMAIN_NAME}àServicesàPath Services

  • Paramétrage de la Connexion Factory pour déclarer le UnitOfOrder côté client.

${DOMAIN_NAME}àServicesàMessagingàJMS Modulesà${JMS_MODULE_NAME}à${CONNECTION_FACTORY}àConfigurationàDefault DeliveryàDefault Unit-Of-Order for Producer:System-generated.

  • Désactivation du Server Affinity pour le LoadBalancing afin qu’un même client répartisse la charge sur le cluster.

${DOMAIN_NAME}àServicesàMessagingàJMS Modulesà${JMS_MODULE_NAME}à${CONNECTION_FACTORY}àConfigurationàLoad BalancingàServer Affinity Enabled:OFF.

  • Déclaration du Path Service dans les Queues.

${DOMAIN_NAME}àServicesàMessagingàJMS Modulesà${JMS_MODULE_NAME}à${QUEUE}àConfigurationàGeneralàAdvancedàUnit-Of-Order Messge Routing:Path Service.

  • Définir la politique de retry en cas d’erreur avec un rejeux d’1 fois (pour l’exemple).

${DOMAIN_NAME}àServicesàMessagingàJMS Modulesà${JMS_MODULE_NAME}à${QUEUE}àConfigurationàGeneralàDelivery Failure
àRedelivery Delay Override=1
àRedelivery Limit=1
àExpiration Policy=Redirect
àError Destination=${QUEUE_ERROR}

  • Mise en place setRollBackOnly dans le code MDB pour mettre la transaction en échec sur une erreur applicative et déclencher le traitement d’erreur du MDB (rejeux ou mise en dead letter)

} catch (Exception e) {
     
      if( alreadyRedelivered ) System.out.println("MDB1 MESSAGE On ERROR -> REPUBLISH ONCE AGAIN : " + msgText);
      else System.out.println("MDB1 MESSAGE On ERROR -> REPUBLISH : " + msgText);
      getMessageDrivenContext().setRollbackOnly();  
     
}

  • Sur QueueOut, positionner le Forward Delay à 1 afin que le consommateur puisse récupérer les messages de la seconde instance (consommation non load-balancé)

${DOMAIN_NAME}àServicesàMessagingàJMS Modulesà${JMS_MODULE_NAME}à${QUEUE}àConfigurationàGeneralàForward Delay:1.
                                                        
TESTS



Dans le domaine mono-instance, le Path Service n’est pas obligatoire pour respecter l’ordre. Par contre sans le paramétrage UnitOfOrder ou du Path Service en cluster, les messages arrivent dans le désordre.

${DOMAIN_NAME}àServicesàMessagingàJMS Modulesà${JMS_MODULE_NAME}à${CONNECTION_FACTORY}àConfigurationàDefault DeliveryàDefault Unit-Of-Order for Producer:None.

Affichage du consommateur avec l’ordre d’arriver sur un consommateur mono thread de 20 messages. Nous pouvons voir que l’ordonnancement des messages n’est pas assuré. Les messages manquants ont été mis dans les dead letter queue suite à la génération d’exception aléatoire.

1 : Threads[0]-No[4] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][30]->EJB2[AdminServer][30]
2 : Threads[0]-No[9] : ->MDB1[AdminServer][50]->EJB1[AdminServer][50]->MDB2[AdminServer][50]->EJB2[AdminServer][50]
3 : Threads[0]-No[14] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][40]->EJB2[AdminServer][50]
4 : Threads[0]-No[8] : ->MDB1[AdminServer][40]->EJB1[AdminServer][50]->MDB2[AdminServer][40]->EJB2[AdminServer][30]
5 : Threads[0]-No[13] : ->MDB1[AdminServer][30]->EJB1[AdminServer][50]->MDB2[AdminServer][30]->EJB2[AdminServer][40]
6 : Threads[0]-No[7] : ->MDB1[AdminServer][30]->EJB1[AdminServer][30]->MDB2[AdminServer][50]->EJB2[AdminServer][40]
7 : Threads[0]-No[3] : ->MDB1[AdminServer][30]->EJB1[AdminServer][30]->MDB2[AdminServer][50]->EJB2[AdminServer][40]
8 : Threads[0]-No[5] : ->MDB1[AdminServer][50]->EJB1[AdminServer][40]->MDB2[AdminServer][40]->EJB2[AdminServer][30]
9 : Threads[0]-No[15] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][50]->EJB2[AdminServer][50]
10 : Threads[0]-No[2] : ->MDB1[AdminServer][30]->EJB1[AdminServer][50]->MDB2[AdminServer][30]->EJB2[AdminServer][30]
11 : Threads[0]-No[10] : ->MDB1[AdminServer][30]->EJB1[AdminServer][30]->MDB2[AdminServer][50]->EJB2[AdminServer][30]
12 : Threads[0]-No[0] : ->MDB1[AdminServer][40]->EJB1[AdminServer][40]->MDB2[AdminServer][30]->EJB2[AdminServer][40]
13 : Threads[0]-No[1] : ->MDB1[AdminServer][30]->EJB1[AdminServer][40]->MDB2[AdminServer][50]->EJB2[AdminServer][40]
14 : Threads[0]-No[19] : ->MDB1[AdminServer][40]->EJB1[AdminServer][30]->MDB2[AdminServer][40]->EJB2[AdminServer][40]

Si l’on remet le UnitOfOrder actif, nous constatons sur le résultat l’ordonnancement des messages malgré les messages écartés par des exceptions système ou applicative.

1 : Threads[0]-No[1] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][30]->EJB2[AdminServer][30]
2 : Threads[0]-No[4] : ->MDB1[AdminServer][40]->EJB1[AdminServer][50]->MDB2[AdminServer][40]->EJB2[AdminServer][30]
3 : Threads[0]-No[12] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][50]->EJB2[AdminServer][40]
4 : Threads[0]-No[16] : ->MDB1[AdminServer][30]->EJB1[AdminServer][30]->MDB2[AdminServer][30]->EJB2[AdminServer][40]
5 : Threads[0]-No[18] : ->MDB1[AdminServer][30]->EJB1[AdminServer][50]->MDB2[AdminServer][40]->EJB2[AdminServer][30]
<!--[if !supportLists]-->1     <!--[endif]-->: Threads[0]-No[19] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][30]->EJB2[AdminServer][40]

Par contre quand on rejoue les messages des dead letter queue (via une procédure manuelle d’administration de mouvement de Queue), l’ordonnancement est perdu.

1 : Threads[0]-No[1] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][30]->EJB2[AdminServer][30]
2 : Threads[0]-No[4] : ->MDB1[AdminServer][40]->EJB1[AdminServer][50]->MDB2[AdminServer][40]->EJB2[AdminServer][30]
3 : Threads[0]-No[12] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][50]->EJB2[AdminServer][40]
4 : Threads[0]-No[16] : ->MDB1[AdminServer][30]->EJB1[AdminServer][30]->MDB2[AdminServer][30]->EJB2[AdminServer][40]
5 : Threads[0]-No[18] : ->MDB1[AdminServer][30]->EJB1[AdminServer][50]->MDB2[AdminServer][40]->EJB2[AdminServer][30]
6 : Threads[0]-No[19] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][30]->EJB2[AdminServer][40]
7 : Threads[0]-No[0] : ->MDB1[AdminServer][40]->EJB1[AdminServer][50]->MDB2[AdminServer][50]->EJB2[AdminServer][40]
8 : Threads[0]-No[6] : ->MDB1[AdminServer][50]->EJB1[AdminServer][50]->MDB2[AdminServer][50]->EJB2[AdminServer][50]
9 : Threads[0]-No[8] : ->MDB1[AdminServer][40]->EJB1[AdminServer][30]->MDB2[AdminServer][40]->EJB2[AdminServer][30]
10 : Threads[0]-No[2] : ->MDB1[AdminServer][30]->EJB1[AdminServer][40]->MDB2[AdminServer][50]->EJB2[AdminServer][40]
11 : Threads[0]-No[3] : ->MDB1[AdminServer][40]->EJB1[AdminServer][40]->MDB2[AdminServer][50]->EJB2[AdminServer][50]
12 : Threads[0]-No[7] : ->MDB1[AdminServer][40]->EJB1[AdminServer][50]->MDB2[AdminServer][50]->EJB2[AdminServer][30]
13 : Threads[0]-No[9] : ->MDB1[AdminServer][30]->EJB1[AdminServer][40]->MDB2[AdminServer][40]->EJB2[AdminServer][30]
14 : Threads[0]-No[10] : ->MDB1[AdminServer][50]->EJB1[AdminServer][30]->MDB2[AdminServer][40]->EJB2[AdminServer][50]
15 : Threads[0]-No[15] : ->MDB1[AdminServer][40]->EJB1[AdminServer][40]->MDB2[AdminServer][40]->EJB2[AdminServer][50]
16 : Threads[0]-No[17] : ->MDB1[AdminServer][40]->EJB1[AdminServer][50]->MDB2[AdminServer][50]->EJB2[AdminServer][50]
17 : Threads[0]-No[13] : ->MDB1[AdminServer][30]->EJB1[AdminServer][50]->MDB2[AdminServer][30]->EJB2[AdminServer][30]
18 : Threads[0]-No[11] : ->MDB1[AdminServer][30]->EJB1[AdminServer][50]->MDB2[AdminServer][40]->EJB2[AdminServer][40]
19 : Threads[0]-No[5] : ->MDB1[AdminServer][30]->EJB1[AdminServer][50]->MDB2[AdminServer][50]->EJB2[AdminServer][50]
20 : Threads[0]-No[14] : ->MDB1[AdminServer][50]->EJB1[AdminServer][40]->MDB2[AdminServer][40]->EJB2[AdminServer][50]

L’ordonnancement est donc conservé tant qu’il n’y a pas rupture de transaction ou mise à l’écart du flux. Le rejeux administratif n’utilise pas le UnitOfOrder, ce qui explique le non-respect de l’ordonnancement (rejeux successif, car génération d’erreur lors de la réinjection des messages)

Même constat en cluster.
 1 : Threads[0]-No[0] : ->MDB1[managed1][30]->EJB1[managed1][30]->MDB2[managed2][30]->EJB2[managed2][40]
2 : Threads[0]-No[1] : ->MDB1[managed1][50]->EJB1[managed1][50]->MDB2[managed1][50]->EJB2[managed1][30]
3 : Threads[0]-No[2] : ->MDB1[managed1][50]->EJB1[managed1][30]->MDB2[managed2][50]->EJB2[managed2][40]
4 : Threads[0]-No[3] : ->MDB1[managed1][50]->EJB1[managed1][30]->MDB2[managed1][30]->EJB2[managed1][30]
5 : Threads[0]-No[4] : ->MDB1[managed1][50]->EJB1[managed1][30]->MDB2[managed2][50]->EJB2[managed2][30]
6 : Threads[0]-No[6] : ->MDB1[managed1][30]->EJB1[managed1][40]->MDB2[managed2][30]->EJB2[managed2][30]
7 : Threads[0]-No[7] : ->MDB1[managed1][30]->EJB1[managed1][30]->MDB2[managed1][40]->EJB2[managed1][30]
8 : Threads[0]-No[9] : ->MDB1[managed1][40]->EJB1[managed1][50]->MDB2[managed1][30]->EJB2[managed1][40]
9 : Threads[0]-No[11] : ->MDB1[managed1][30]->EJB1[managed1][30]->MDB2[managed2][50]->EJB2[managed2][30]
10 : Threads[0]-No[13] : ->MDB1[managed1][40]->EJB1[managed1][30]->MDB2[managed2][40]->EJB2[managed2][40]
11 : Threads[0]-No[15] : ->MDB1[managed1][30]->EJB1[managed1][40]->MDB2[managed2][30]->EJB2[managed2][30]
12 : Threads[0]-No[17] : ->MDB1[managed1][40]->EJB1[managed1][50]->MDB2[managed2][40]->EJB2[managed2][40]
13 : Threads[0]-No[18] : ->MDB1[managed1][40]->EJB1[managed1][30]->MDB2[managed1][50]->EJB2[managed1][30]
14 : Threads[0]-No[19] : ->MDB1[managed1][40]->EJB1[managed1][40]->MDB2[managed2][50]->EJB2[managed2][30]
15 : Threads[0]-No[10] : ->MDB1[managed1][30]->EJB1[managed1][30]->MDB2[managed1][50]->EJB2[managed1][50]
16 : Threads[0]-No[8] : ->MDB1[managed1][30]->EJB1[managed1][30]->MDB2[managed2][30]->EJB2[managed2][40]
17 : Threads[0]-No[14] : ->MDB1[managed1][40]->EJB1[managed1][40]->MDB2[managed1][50]->EJB2[managed1][40]
18 : Threads[0]-No[16] : ->MDB1[managed1][40]->EJB1[managed1][50]->MDB2[managed1][50]->EJB2[managed1][40]
19 : Threads[0]-No[5] : ->MDB1[managed1][40]->EJB1[managed1][50]->MDB2[managed1][30]->EJB2[managed1][30]
20 : Threads[0]-No[12] : ->MDB1[managed1][50]->EJB1[managed1][50]->MDB2[managed1][50]->EJB2[managed1][30]

CONCLUSION



La fonctionnalité UnitOfOrder permet d’ordonnancer les messages JMS par producteur avec la contrainte d’avoir une ressource Singleton en cluster (prévoir migration).

Lors d’erreur technique et applicative, les messages mis à l’écart ne sont plus garantis pour l’ordonnancement (retiré de la liste ordonnée reçue par le consommateur). Lors de leurs réinjections, ils sont reçus désynchronisés (après les messages ordonnés), ce qui peut poser un problème si l’ordre initial doit être respecté.

Aucun mécanisme ne peut garantir l’ordonnancement des messages en erreurs, car nécessitant le blocage complet des flux le temps de la réinjection. Il faudra donc prévoir un scénario de rejeux applicatif afin de rollbacker l’ensemble des messages associés au flux et les rejouer.

La fonctionnalité UnitOfOrder permet par contre de garantir les flux hors événement d’erreur et de s’assurer du bon traitement ordonné sur un ensemble de messages d’un client. 

0 commentaires:

AUTEUR

Ma photo
Carrières Sur Sein, Yvelines, France
Consultant Oracle (Ancien consultant BEA depuis 2001), je m’occupe des expertises sur les produits Oracle : SOCLE (Weblogic, Coherence, JRockit) SOA (Service Bus, SOA Suite, BPM)
MON CV

LABEL 3D

Blogumulus by Roy Tanck and Amanda Fazani

LABEL CLOUD

MAP

Locations of visitors to this page

AUTRES BLOG

LIVRES

MEMBRES