Lorsque vous utilisez des routes Apache Camel pour votre intégration et qu’un problème se produit, une démarche classique consiste à réessayer l’envoi du message. C’est particulièrement le cas pour les erreurs  plutôt « temporaires ». Par exemple, si vous avez une panne de réseau, vous tenterez de renvoyer les messages, dans l’attente d’un retour de réseau.

Dans Apache Camel, cette règle de redistribution est configurée dans le gestionnaire d’erreurs. Ce dernier peut effectivement prendre en charge de telles erreurs.
Cependant, par défaut, le message à redistribuer est stocké en mémoire. De ce fait, les nouveaux échanges ne sont pas transmis, car le premier n’est pas signalé comme «pris en charge» !

Cela pose souci et perte du message dans le cas, par exemple, d’un redémarrage du conteneur hébergeant la route Camel (comme Apache Karaf). De plus, en termes de performances, il est intéressant d’assurer une continuité des échanges.

Plusieurs palliatifs existent, et nous allons justement illustrer dans cet article une stratégie de redelivery exponentiel.

 

Les routes Apache Camel

 

Commençons par des routes pour illustrer notre cas. Nous utilisons le DSL Camel Blueprint dans cet exemple, car notre conteneur préféré est Apache Karaf (évidemment 😉) !

La première route est assez simple: elle écoute les requêtes HTTP entrantes (à l’aide du composant Camel Jetty), les convertit en strings et les stocke dans une queue JMS.

La deuxième route lit des messages de la file d’attente JMS, prépare un JSON utilisant un processeur (enregistré en tant que service dans Karaf) et appelle un service REST.

 

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:cxf="http://camel.apache.org/schema/cxf">
 
  <reference id="connectionFactory" interface="javax.jms.ConnectionFactory"/>
  <reference id="jsonProcessor" interface="org.apache.camel.Processor" filter="(name=json)"/>
 
  <cxf:rsClient id="rsClient" address="http://localhost:8181/cxf/test/"/>
 
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">
     <route id="first">
        <from uri="jetty:http://0.0.0.0:9090/first"/>
        <convertBodyTo type="java.lang.String"/>
        <wireTap uri=jms:queue:second?connectionFactory=connectionFactory"/>
        <setBody><constant>OK</constant></setBody>
     </route>
     <route id="second">
       <from uri="jms:queue:second?connectionFactory=connectionFactory"/>
       <process ref="jsonProcessor"/>
       <setHeader headerName="operationName"><constant>updateResource</constant></setHeader>
       <setHeader headerName="CamelCxfRsUsingHttpAPI"><constant>false</constant></setHeader>
       <setHeader herderName="CamelAcceptContentType"><constant>application/json</constant></setHeader>
       <to uri="cxfrs://bean://rsClient?synchronous=true"/>
     </route>
  </camelContext>
 
</blueprint>

Redelivery exponentiel

 

Par défaut, lorsque vous créez une route Camel, ce dernier active automatiquement le gestionnaire d’erreurs par défaut. Celui-ci intercepte alors un échange, lorsqu’il contient une exception.

Après avoir donc arrêté le message en erreur, il attend le signalement de prise en charge et réessaie l’échange.

 

Dans notre cas, nous souhaiterions :
  1. Conserver le message pour pouvoir le redistribuer, même en cas de redémarrage ou d’erreur de la plateforme ;
  2. Supprimer le message de la file d’attente JMS uniquement lorsqu’il a réellement été traité avec succès ;
  3. Relancer le message plusieurs fois, ou à l’infini ;

Pour ce qui est du point N°1, il est déjà pris en charge dans Camel, qui met les messages JMS en persistants par défaut. Cela signifie donc que les messages sont stockés dans la mémoire de persistance kahadb d’ActiveMQ.
Même si nous redémarrons ActiveMQ, ces messages ne sont pas perdus.

Dans notre besoin N°2, nous modifions le mode d’ACK (acquittement) JMS. Par défaut, Camel utilise auto ack: cela signifie que l’ack est envoyé au broker dès que le message est lu, donc juste après l’endpoint JMS de la route Camel.
Si un échec a lieu dans la route Camel, le message est déjà supprimé de la file d’attente … et vous devez donc faire avec !

Mais, en basculant le mode ack pour ack client, nous décidons d’accuser réception uniquement à l’heure d’achèvement de l’échange. Ainsi, le message ne sera retiré de la file d’attente que s’il a été traité complètement et avec succès.

Voici comment nous modifions le mode d’ack sur l’URI de l’endpoint JMS :

 

<from uri="jms:queue:second?connectionFactory=connectionFactory&amp;acknowledgementModeName=CLIENT_ACKNOWLEDGE"/>

 

Et enfin, pour notre 3ième point, nous allons modifier l’URL fournisseur dans la connection factory d’ActiveMQ. Par défaut, la factory ActiveMQ redistribue un message 7 fois au maximum.
Le nombre de tentatives de redelivery peut être modifié sur l’URL utilisée par la connection factory.
Par exemple, pour définir un nombre maximum de redistributions à 10, vous pouvez faire :

 

<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="tcp://localhost:61616?jms.redeliveryPolicy.maximumRedeliveries=10" />
</bean>

 

Vous pouvez également redistribuer un message indéfiniment (jusqu’à la date d’expiration du message, en réalité), en utilisant -1 pour maximumRedeliveries:

 

<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="tcp://localhost:61616?jms.redeliveryPolicy.maximumRedeliveries=-1" />
</bean>

Backoff delivery avec ActiveMQ

 

Dans la section précédente, nous avons donc utilisé une stratégie de redelivery via ActiveMQ. C’est très pratique, mais dans un souci d’optimisation du nombre de tentatives, il peut s’avérer souhaitable de mettre en place un certain délai entre les deliveries.
Et plus nous avons des redistributions, plus nous voulons augmenter ce délai. C’est ce que nous appelons le backoff delivery.

Cela se configure également dans la connection factory en utilisant :

  • useExponentialBackOff permet un backoff exponentiel. Il est désactivé par défaut ;
  • backOffMultiplier : permet de choisir la façon d’augmenter le délai. Par défaut, le «nouveau» délai est 5 fois plus élevé que le «précédent» ;
  • initialRedeliveryDelay est le délai de delivery initial. C’est le temps de démarrage du backoff. Par défaut, c’est 1000L, soit 1 seconde ;
  • maximumRedeliveryDelay est le délai maximum que nous pouvons avoir. Par défaut, c’est -1, ce qui signifie qu’il n’y a pas maximum ;

Voici donc comment mettre à jour l’URL de la connection factory pour permettre un backoff delivery :

 

<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="tcp://localhost:61616?jms.redeliveryPolicy.maximumRedeliveries=-1&amp;jms.redeliveryPolicy.useExponentialBackOff=true&amp;jms.redeliveryPolicy.initialRedeliveryDelay=2000L&amp;jms.redeliveryPolicy.backOffMultiplier=2" />
</bean>

 

Dans cet exemple, nous avons une politique de backoff delivery qui doublera le délai pour chaque tentative, en commençant par 2 secondes (2000L).

Grâce à tout cela, nous conservons donc une route Camel assez simple, et bénéficions d’une redistribution correcte et exponentielle, avec gestion du backoff.

 

Merci à Jean-Baptiste Onofré (blog.nanthrax.net/?p=820 – twitter.com/jbonofre)

Jean-Baptiste Onofré travaille quotidiennement sur les projets Apache utilisés dans divers produits. Technical Advisor, membre de l’Apache Software Foundation, il est également committer et membre PMC (Project Management Committee) d’une vingtaine de projets Apache, et a travaillé sur des projets majeurs comme Archiva, Camel, Karaf ou encore ServiceMix.

 

Photo d’illustration de la publication par Andrey Larin via Unsplash.

 

.

Les articles que nous rédigeons sont le fruit 🍏 de plusieurs heures de dur labeur. Ainsi, nous vous serions reconnaissants de pas plagier nos travaux qui sont protégés par le droit d’auteur 📜. Toutefois, vous êtes libres de les partager ou de nous faire part de vos remarques.

Si vous avez trouvé une erreur sur cette page, vous pouvez nous en informer en sélectionnant le texte en question, et en appuyant sur Ctrl + Entrée. Merci 🙂 !

Redelivery exponentiel avec Camel et ActiveMQ
5 (100%) 2 vote[s]

Rapport de faute d’orthographe

Le texte suivant sera envoyé à nos rédacteurs :