vendredi 2 septembre 2011

Store-and-Forward ou SAF a été introduite en version 9 de Weblogic est à pour objectif d’offrir un service via un agent d’envoi de message sur un provider JMS Weblogic distance (en version 9 ou plus).  L’objectif étant de rendre robuste et transparent l’envoi des messages même si le provider distant n’est pas là ou s’il y a des problèmes de communications (via des files internes avec rejeux). Il assure également l’ordonnancement des messages.

Il supporte le clustering nativement via les destinations distribuées, mais ne support pas les destinations temporaires (JMSReplyTo). L’agent SAF peut être migré sur une instance valide lors d’incident.

Ce service offre également cette fonctionnalité sur la partie cliente (Java Standalone).

Il est optimisé pour le mode transactionnel via des mécanismes internes qui ne nécessitent pas l’utilisation de transaction distribuée (propagé sur les destinations).

On peut voir le SAF agent comme un serveur JMS avec toutes les fonctionnalités associées, mais basées que sur des destinations importée et non locale (rendre local une ressource distante comme pour l’appel d’un EJB).

  
Les ressources SAF à déclarer sont :

SAF Agent
Envoi de message à la destination distante. Stocke le message localement et le retransmet quand le message d’acquittement ne revient pas à temps.
Remote SAF Context
URL et credential de la ressource distante hébergeant les remote destination.
SAF error-handling
Définition de la stratégie de gestion d’erreurs lors des échecs d’envoi (pas obligatoire).
Imported SAF Destination
Alias local des destinations JMS distantes.

(Un SAF Remote Context ou SAF error-handling peut être associé à plusieurs Imported SAF Destination)

Vous pouvez utiliser SAF pour délivrer des messages :

ü  Entre deux instances Weblogic standalone.
ü  Entre instances Weblogic d’un cluster.
ü  Entre deux clusters dans un même domaine.
ü  Entre deux domaines.

SAF peut être utilisé par :

ü  Des applications JMS
ü  Des Web Service type Reliable Messaging (WS-RM)

JMS SAF ne requiert qu’un agent d’envoi sur la partie émettrice, contrairement au WS-RM SAF qui lui requière deux agents. Un d’envoi et l’autre de réception.

ü Sending agent       Utilisé pour les messages JMS et WSRM. Si la persistance de message est requise, un agent d’envoi stocke le message localement et envoie le message tant que la partie distante est disponible. Il la retransmet dans le cas où il n’y a pas eu d’acquittement à temps.

ü Receiving agent   Utilisé seulement pour WS-RM. Il détecte est éliminé les messages dupliqués transmis par un agent d’envoi, et délivre le message à une destination finale.

Dans le cas d’un message JMS, le serveur JMS associé avec la destination JMS distante exportée sur la réception du message gère les envois doublon. C’est pour cela qu’il n’y a pas besoin de propager la transaction et donc allège les traitements.

CINEMATIQUE





1 – Le producteur JMS local créer un message à envoyer à un consommateur JMS distant.
2 – Le producteur JM local envoie le message à une destination JMS SAF importé.
3 – L’agent d’envoi SAF prend le message de la destination SAF importé.
4 – Si le endpoint distant de réception est accessible, le SAF Agent l’envoie à la destination distante. Dans le cas contraire, le message est stoqué dans une file persistante interne locale de l’instance Weblogic jusqu’a ce que le endpoint soit accessible.
5 – Le consommateur JMS distant consume le message de la destination distante.

PARAMETRAGE



L’objectif du SAF et d’exposer une file distante comme si elle été en local en garantissant une robustesse de l’envoi via une file interne. On définit donc une ressource SAF comme si on définissait un serveur JMS en lui associant une destination non plus local, mais importé. Il faut donc avoir une file distante. On définira également une file distante d’erreurs pour recevoir les messages en expiration ou en échec.

  
SAF Agent

Lors de la configuration de l’agent SAF, vous pouvez choisir d’utiliser un FileStore ou utiliser celui par défaut (par défaut). Dans le cas d’un cluster, il faudra prendre celui par défaut.

On définit en premier lieu le SAF Agent.


 consoleàServicesàMessagingàStore-and-Forward AgentsàNewà’SAFAgent’


cd(’/’)
cmo.createSAFAgent(‘SAFAgent')
cd('/SAFAgents/ SAFAgent')
set('Targets',jarray.array([ObjectName
('com.bea:Name=AdminServer,Type=Server')], ObjectName))
set('ServiceType','Both')

RESSOURCES LOCALES

On définit ensuite un serveur JMS local sur l’instance WLS pour recevoir une factory de connexion.

consoleàServicesàMessagingàJMS ServersàNewà’JMSServer-Local’

Nous déclarons un module JMS pour héberger toutes les ressources JMS en déclarant deux SubModule. Un en target sur le serveur JMS local, l’autre sur l’agent SAF.

consoleàServicesàMessagingàJMS ModulesàNewà’SystemModule-SAF’
consoleàServicesàMessagingàJMS ModulesàSystemModule-SAFàSubdeploymentsà’Sub_LOCAL’ (target : ’JMSServer-Local’)
consoleàServicesàMessagingàJMS ModulesàSystemModule-SAFàSubdeploymentsà’Sub_SAF’ (target : ‘SAFAgent’)

Nous créons une Connection Factory associée au Sub_LOCAL.

consoleàServicesàMessagingàJMS ModulesàSystemModule-SAFàNewàConnection FactoryàConnectionFactory_Sender’

Remote SAF Contexte

Ensuite, nous créons un Remote SAF Contexte afin de préciser comment accéder au domaine distant (URL, credential, … ).


consoleàServicesàMessagingàJMS ModulesàSystemModule-SAFàNewà Remote SAF Contextà’RemoteSAFContext-SAF’


Dans le cas d’un cluster, préciser la liste des instances associées (ex : t3://localhost:8101,t3://localhost:8201).

SAF Error Handling

Nous créons un SAF Error Handling.


consoleàServicesàMessagingàJMS ModulesàSystemModule-SAFàNewà ErrorHandling-SAFà
ErrorHandling-SAF’

cmo.createSAFErrorHandling(’’)
cd(’/JMSSystemResources//JMSResource/
module>/SAFErrorHandlings/’)
set('Policy',‘')

Imported SAF Destination

Puis nous associons le contexte et le Error Handler dans un Remote SAF Context afin de déclarer les files distantes.
  

consoleàServicesàMessagingàJMS ModulesàSystemModule-SAFàNewàSAF Imported Destinationsà SAFImportedDestinations-SAF’

  
cd(’/JMSSystemResources//JMSResource/’)
cmo.createSAFRemoteContext(‘')
cd('/JMSSystemResources//JMSResource/
module>/SAFRemoteContexts[]/
name>/SAFLoginContext/')
set('LoginURL','t3://:')

On y créer ensuite les destinations distantes en précisant un nom JNDI Local et celui de la destination distante.


consoleàServicesàMessagingàJMS ModulesàSystemModule-SAFàSAFImportedDestinations-SAFàQueuesàNewà’SAFQueue-Sender’ (JNDI Local et Remote : ‘Queue_Receiver’)


consoleàServicesàMessagingàJMS ModulesàSystemModule-SAFàSAFImportedDestinations-SAFàQueuesàNewà’SAFQueue-Error’ (JNDI Local et Remote : ‘Queue_Error’)


To create an imported SAF destination, type:
cd(’/JMSSystemResources//JMSResource/< JMS module>’)
cmo.createSAFImportedDestinations(‘')
set('TimeToLiveDefault',‘
set('UseSAFTimeToLiveDefault',‘')
cd('/JMSSystemResources//SubDeployments/
factory subdeployment name>')

To create a SAF queue, type:
cd(’/JMSSystemResources//JMSResource/
module>/SAFImportedDestinations/’)
cmo.createSAFQueue(‘')
cd(‘/’)
cd('/JMSSystemResources//JMSResource/
module>/SAFImportedDestinations//
module>/SAFQueues/')
set('RemoteJNDIName',‘')
set('LocalJNDIName',‘')

To set the remote SAF context and SAF error handling resource in imported
SAF destination, type:
cd(’/JMSSystemResources//JMSResource/
module>/SAFImportedDestinations/’)
cmo.setSAFRemoteContext(getMBean(’/JMSSystemResources/
module>/JMSResource//SAFRemoteContexts/
context name>’))
cd(’/’)
cd(’/JMSSystemResources/sourceJMSModule/JMSResource/
module>/SAFErrorHandlings/’)
cmo.setSAFErrorDestination(getMBean(’/JMSSystemResources/
module>/JMSResource/
module>/SAFImportedDestinations//SAFQueues/
queue name>/SAFErrorHandling/’))
cd(’/JMSSystemResources//JMSResource/
module>/SAFImportedDestinations/’)
cmo.setSAFErrorHandling(getMBean(’/JMSSystemResources/sourceJMSModu
le/JMSResource/sourceJMSModule/SAFErrorHandlings/
handling>’))

On peut maintenant déclarer la file d’erreurs pour gérer les messages en expiration ou en échec de traitement.


consoleàServicesàMessagingàJMS ModulesàSystemModule-SAFà
ErrorHandling-SAF


CLUSTER



Dans le cas d’un cluster, il faut juste déclarer la file distante en distribué et renseigner dans le contexte SAF la liste des instances du cluster. Côté client, il faudra déclarer autant de serveurs SAF que d’instance et déclarer un SubDeployment sur ces serveurs JMS. Tous les objets déployés SAF seront visibles de l’ensemble des instances du cluster client.

TRANSACTION



MONITORING



DEBUGING



En phase de test, vous pouvez ajouter les paramètres suivants au démarrage du serveur afin de pouvoir contrôler le fonctionnement interne des agents SAF :

    -Dweblogic.debug.DebugSAFSendingAgent=true
    -Dweblogic.log.StdoutSeverity="Debug"

SECURITY



CLIENT



La fonctionnalité d’indirection est conservée dans la partie cliente standalone via le SAF Client Agent. Elle permet au client de ne pas être interrompu dans l’envoi des messages même si la destination serveur n’est plus disponible (passivation des messages dans un fichier local au client). On pourra également activer ou non l’envoi de message administrativement via une API dédié.


                           
Pour créer un client SAF, il faut :

ü  Générer un fichier JMS SAF Client configuration.
ü  Encrypter le mot de passe de la destination (si credential utilisé).
ü  Déployer le JAR client SAF sur la machine client.
ü  Modifier le code client JMS pour utiliser le provider JNDI du client SAF.

JMS SAF Client Configuration File

Le fichier client SAF est nécessaire sur chaque machine client et peut être généré à partir de l’utilitaire ClientSAFGenerate de la distribution Weblogic. Ce fichier reprend tous les artefacts JMS issus de la configuration du domaine nécessaire à la connexion et l’envoi de message. Il se conforme au schéma weblogic-jms.xsd. Pour la génération, placer vous au niveau du répertorie de configuration JMS du domaine :

Exemple d’appel unix
. ${WL_HOME}/server/bin/setWLSEnv.sh
cd ${DOMAINE_HOME}/config/jms
java weblogic.jms.extensions.ClientSAFGenerate -url http://${IP_ADMIN}:${PORT_ADMIN} -username ${USERNAME} -moduleFile ${JMS_MODULE_NAME}.xml -outputFile ${CLIENT_REP}/ClientSAF-jms.xml

scripte DOS
@CLS
@echo OFF
@SETLOCAL

@set WL_HOME=D:\PRODUCT\MIDDLEWARE\SOA\SOA_11.1.1.4\FMW_11.1.1.4\wlserver_10.3
@set JAVA_HOME=D:\PRODUCT\MIDDLEWARE\SOA\SOA_11.1.1.4\FMW_11.1.1.4\jrockit_160_22_D1.1.1-3
@set DOMAINE_HOME=D:\DOMAINE\TEST\SAF\DOMAINES\cluster_domain
@set IP_ADMIN=localhost
@set PORT_ADMIN=7001
@set USERNAME=weblogic
@set JMS_MODULE_NAME=systemmodule-jms
@set CLIENT_REP=D:\DOMAINE\TEST\SAF\WORKSPACE

@set CLASSPATH=%JAVA_HOME%\lib\tools.jar;%WL_HOME%\server\lib\weblogic.jar;%CLASSPATH%
@set PATH=%JAVA_HOME%\bin;%PATH%

cd %DOMAINE_HOME%\config\jms

java weblogic.jms.extensions.ClientSAFGenerate -url http://%IP_ADMIN%:%PORT_ADMIN% -username %USERNAME% -moduleFile %JMS_MODULE_NAME%.xml -outputFile %CLIENT_REP%/ClientSAF-jms.xml

Vous obtenez le fichier suivant sans information de mot de passe :

ClientSAF-jms.xml
 <?xml version='1.0' encoding='UTF-8'?>  
 <weblogic-client-jms xmlns="http://xmlns.oracle.com/weblogic/weblogic-jms" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-jms http://xmlns.oracle.com/weblogic/weblogic-jms/1.1/weblogic-jms.xsd">  
  <connection-factory name="ConnectionFactory">  
   <default-targeting-enabled>false</default-targeting-enabled>  
   <jndi-name>ConnectionFactory</jndi-name>  
   <client-params>  
    <client-id-policy>Restricted</client-id-policy>  
    <subscription-sharing-policy>Exclusive</subscription-sharing-policy>  
    <messages-maximum>10</messages-maximum>  
   </client-params>  
   <transaction-params>  
    <xa-connection-factory-enabled>true</xa-connection-factory-enabled>  
   </transaction-params>  
   <load-balancing-params>  
    <load-balancing-enabled>true</load-balancing-enabled>  
    <server-affinity-enabled>false</server-affinity-enabled>  
   </load-balancing-params>  
   <security-params>  
    <attach-jmsx-user-id>false</attach-jmsx-user-id>  
   </security-params>  
  </connection-factory>  
  <saf-imported-destinations name="systemmodule">  
   <saf-remote-context>RemoteContext0</saf-remote-context>  
  </saf-imported-destinations>  
  <saf-remote-context name="RemoteContext0">  
   <saf-login-context>  
    <loginURL>http://localhost:7001</loginURL>  
    <username>weblogic</username>  
   </saf-login-context>  
  </saf-remote-context>  
 </weblogic-client-jms>  

Pour ce connecter à la destination, il faut générer un mot de passe encrypté en plus du fichier client via la commande suivante.

@CLS
@echo OFF
@SETLOCAL

@set WL_HOME=D:\PRODUCT\MIDDLEWARE\SOA\SOA_11.1.1.4\FMW_11.1.1.4\wlserver_10.3
@set JAVA_HOME=D:\PRODUCT\MIDDLEWARE\SOA\SOA_11.1.1.4\FMW_11.1.1.4\jrockit_160_22_D1.1.1-3

@set CLASSPATH=%JAVA_HOME%\lib\tools.jar;%WL_HOME%\server\lib\weblogic.jar;%CLASSPATH%
@set PATH=%JAVA_HOME%\bin;%PATH%

@set KEY_PASSWORD=weblogic
@set REMOTE_PASSWORD=weblogic1

java -Dweblogic.management.allowPasswordEcho=true weblogic.jms.extensions.ClientSAFEncrypt %KEY_PASSWORD% %REMOTE_PASSWORD%

Chaîne généré :
{Algorithm}PBEWithMD5AndDES{Salt}R4ehGlzcY4w={Data}m1d8ZQURd4/4soXMBphHSJhIxDvFZ0Sv

Il faut ensuite insérer cette chaîne dans le fichier SAF client au niveau du remote-context.
ABCD

Attention, dans mon test, j’ai dû rajouter en dur les files qui n’ont pas été générées par le scripte. L’ajout de préfixe pour le nom JNDI Local n’a pas fonctionné, car non reconnu par le client SAF ?!.


JAR SAF Client

Le client SAF nécessite la présente dans son CLASSPATH des JAR wlsafclient.jar, wljmsclient.jar, wlclient.jar.  Le JAR wljmsclient.jar a la référence des autres JAR, donc il suffit de mettre celui-ci dans le CLASSPATH (si cela ne fonctionne pas, placer les 3).

Vous pouvez également générer et utiliser le wlfullclient.jar plus gros, mais qui permet d’utiliser les commandes ClientSAFGenerate en local sur la machine client.

Modification Client JMS

Modifier le paramétrage JMS pour qu’il utiliser la factory de connexion suivante avec comme URL le fichier SAF Client :

java.naming.factory.initial is weblogic.jms.safclient.jndi.InitialContextFactoryImpl

Exemple de paramètre d’un client JMS standard.
Context.PROVIDER_URL=t3://instance1:7001,instance2:7001
Context.SECURITY_PRINCIPAL=weblogic
Context.SECURITY_CREDENTIALS=welcome1
Context.INITIAL_CONTEXT_FACTORY=weblogic.jndi.WLInitialContextFactory

QUEUE_CONNECTION_FACTORY=jms/yourQueueConnectionFactory
QUEUE_NAME=jms/yourQueue

Exemple de paramètre d’un client JMS SAF.
Context.PROVIDER_URL=file:/home/Projects/ SAFClient.xml
Context.SECURITY_PRINCIPAL=weblogic
Context.SECURITY_CREDENTIALS=technobabble
Context.INITIAL_CONTEXT_FACTORY=weblogic.jms.safclient.jndi.InitialContextFactoryImpl

QUEUE_CONNECTION_FACTORY=jms/yourQueueConnectionFactory
QUEUE_NAME=jms/yourQueue

Test de Robustesse

Lors de la connexion du client, celui-ci commence à envoyer et persister les messages jusqu’a la connexion de l’agent.

Affichage de la sortie standard du client SAF.
Send essage : Threads[0]-No[17] :
Send essage : Threads[0]-No[18] :
Send essage : Threads[0]-No[19] :
Send essage : Threads[0]-No[20] :
Agent "ClientSAFAgent0" got connected to RemoteContext0 while processing messages for DistributedQueueError
Agent "ClientSAFAgent0" got connected to RemoteContext0 while processing messages for DistributedQueue_Dst
Send essage : Threads[0]-No[21] :
Send essage : Threads[0]-No[22] :

Une fois la connexion établie, les messages sont poussés par le SAF Server à la destination distante. Lors de la perte de la partie serveur (kill), un message d’erreur apparait côté client, mais celui-ci continu d’envoyer les messages persister dans le FileStore local client.

Erreur lors de la rupture
Agent "ClientSAFAgent0" lost the connection to RemoteContext0 while processing messages for DistributedQueue_Dst
Stack level 0
weblogic.jms.forwarder.Forwarder$Subforwarder$EndpointNotAvailableException
       at weblogic.jms.forwarder.Forwarder$Subforwarder.getEndpoint(Forwarder.java:1085)
       at weblogic.jms.forwarder.Forwarder$Subforwarder.forward(Forwarder.java:782)
       at weblogic.jms.forwarder.Forwarder$Subforwarder.pushMessages(Forwarder.java:716)
       at weblogic.messaging.util.DeliveryList.run(DeliveryList.java:263)
       at weblogic.work.ExecuteRequestAdapter.execute(ExecuteRequestAdapter.java:22)
       at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:145)
       at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:117)
disconnect stack trace finished

Envoie des messages malgré  la perte de la connexion.
Send essage : Threads[0]-No[85] :
Send essage : Threads[0]-No[86] :
Send essage : Threads[0]-No[87] :
Send essage : Threads[0]-No[88] :
Send essage : Threads[0]-No[89] :
Send essage : Threads[0]-No[90] :
Send essage : Threads[0]-No[91] :

Lors des retry, des messages apparaissent régulièrement jusqu'à la reprise du service SAF Server.

Agent "ClientSAFAgent0" lost the connection to RemoteContext0 while processing messages for DistributedQueue_Dst
Stack level 0
javax.naming.CommunicationException [Root exception is java.rmi.ConnectException: Could not establish a connection with 6790521546803876601S:localhost:[7201,-1,-1,-1,-1,-1,-1]:cluster_domain:managed2, java.io.IOException: Destination unreachable; nested exception is:
       java.net.ConnectException: Connection refused: connect; No available router to destination; nested exception is:
       java.io.IOException: Destination unreachable; nested exception is:
       java.net.ConnectException: Connection refused: connect; No available router to destination]
       at weblogic.jndi.internal.ExceptionTranslator.toNamingException(ExceptionTranslator.java:64)
       at weblogic.jndi.internal.WLContextImpl.translateException(WLContextImpl.java:470)
       at weblogic.jndi.internal.WLContextImpl.lookup(WLContextImpl.java:426)
       at weblogic.jndi.internal.WLContextImpl.lookup(WLContextImpl.java:411)
       at javax.naming.InitialContext.lookup(InitialContext.java:392)
       at weblogic.jms.forwarder.Forwarder.connectTarget(Forwarder.java:416)
       at weblogic.jms.forwarder.Forwarder.reconnect(Forwarder.java:246)
       at weblogic.jms.forwarder.Forwarder.timerExpired(Forwarder.java:311)
       at weblogic.timers.internal.TimerImpl.run(TimerImpl.java:273)
       at weblogic.work.ExecuteRequestAdapter.execute(ExecuteRequestAdapter.java:21)
       at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:145)
       at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:117)
Caused by: java.rmi.ConnectException: Could not establish a connection with 6790521546803876601S:localhost:[7201,-1,-1,-1,-1,-1,-1]:cluster_domain:managed2, java.io.IOException: Destination unreachable; nested exception is:
       java.net.ConnectException: Connection refused: connect; No available router to destination; nested exception is:
       java.io.IOException: Destination unreachable; nested exception is:
       java.net.ConnectException: Connection refused: connect; No available router to destination

Lors de la restauration de la connexion, nous avons le message suivant

Send essage : Threads[0]-No[183] :
Send essage : Threads[0]-No[184] :
Agent "ClientSAFAgent0" got connected to RemoteContext0 while processing messages for DistributedQueue_Dst
Send essage : Threads[0]-No[185] :
Send essage : Threads[0]-No[186] :

Sur le consommateur, nous avons l’apparition des messages avec un creux correspondant à l’interruption du serveur SAF.

71 : Threads[0]-No[72] :
72 : Threads[0]-No[73] :
73 : Threads[0]-No[74] :
74 : Threads[0]-No[75] :
75 : Threads[0]-No[76] :
76 : Threads[0]-No[78] :
77 : Threads[0]-No[77] :

78 : Threads[0]-No[189] :
79 : Threads[0]-No[191] :
80 : Threads[0]-No[193] :
81 : Threads[0]-No[195] :
82 : Threads[0]-No[197] :

Les messages non transmis sont ensuite renvoyés de temps en temps dans le flux courant.

94 : Threads[0]-No[221] :
95 : Threads[0]-No[223] :
96 : Threads[0]-No[80] :
97 : Threads[0]-No[225] :
98 : Threads[0]-No[227] :



1 commentaires:

Hugues Simonnet a dit…

C'est magnifique et super bien expliqué ! Bravo

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