Messagerie et streaming d'événements

ActiveMQ Classic 6 : installation, activemq.xml et configuration des transports

Intermédiaire11 min de lectureMis à jour

Cette page a été traduite de l'anglais avec l'aide de l'IA, puis vérifiée par rapport à l'original. Lire la version anglaise

ActiveMQ Classic est le broker JMS historique du projet Apache ActiveMQ. Ce guide vous mène d'une archive téléchargée à un broker dont vous comprenez le fonctionnement : l'arborescence de la distribution, les sections d'activemq.xml qui comptent, les transport connectors et les protocoles, les limites d'utilisation système et le producer flow control, les URL de failover côté client, les brokers embarqués et les réseaux de brokers — en signalant au passage ce qui change avec ActiveMQ Artemis.

Ce que vous allez apprendre

  • Installer et exécuter ActiveMQ Classic 6 comme service sous Linux et dans un conteneur
  • Lire et modifier en toute confiance les principales sections de conf/activemq.xml
  • Configurer des transport connectors pour les clients OpenWire, AMQP, STOMP, MQTT et WebSocket
  • Fixer les limites de mémoire, de stockage et d'espace temporaire, et comprendre le producer flow control
  • Écrire des URL de connexion failover et régler le prefetch côté client
  • Relier des brokers en réseau et savoir quand Artemis est le meilleur choix

Apache ActiveMQ Classic est un broker JMS mûr écrit en Java : vous le téléchargez, vous le démarrez, et quelques secondes plus tard vous disposez de files, de topics, d'une console web et de listeners pour cinq protocoles. Cette simplicité est bien réelle, mais une mise en production repose sur une poignée de choix de configuration dans conf/activemq.xml que les valeurs par défaut ne font pas à votre place. Ce guide couvre la branche 6.x, qui exige Java 17 et utilise l'API jakarta.jms, et signale les points sur lesquels ActiveMQ Artemis — l'autre broker du projet — procède autrement.

Installer le broker

ActiveMQ est livré sous forme d'une archive unique contenant le broker, ses bibliothèques et une console web. Il n'y a ni installeur ni base de données à préparer.

Installez un JDK 17 ou plus récent. Sous Debian ou Ubuntu, sudo apt install openjdk-17-jre-headless ; sur les systèmes de la famille RHEL, sudo dnf install java-17-openjdk-headless. Vérifiez avec java -version.

Créez un utilisateur de service. Faites tourner le broker sous un compte non privilégié : sudo useradd --system --home /opt/activemq --shell /usr/sbin/nologin activemq.

Téléchargez et décompressez. Récupérez l'archive binaire sur la page de téléchargement Apache, vérifiez sa signature ou sa somme de contrôle, puis décompressez-la sous /opt en gardant un lien symbolique indépendant de la version, afin qu'une mise à jour se résume à changer ce lien :

curl -LO https://archive.apache.org/dist/activemq/6.1.6/apache-activemq-6.1.6-bin.tar.gz
sudo tar -xzf apache-activemq-6.1.6-bin.tar.gz -C /opt
sudo ln -sfn /opt/apache-activemq-6.1.6 /opt/activemq
sudo chown -R activemq:activemq /opt/apache-activemq-6.1.6

Lancez-le une première fois au premier plan. /opt/activemq/bin/activemq console exécute le broker attaché à votre terminal, ce qui permet de lire le journal de démarrage. Vous devez y voir des lignes annonçant le store KahaDB, chaque transport connector (tcp://0.0.0.0:61616, amqp://..., etc.) et la console web sur http://0.0.0.0:8161/. Arrêtez-le avec Ctrl+C.

Exécutez-le comme service systemd. Créez /etc/systemd/system/activemq.service, puis lancez sudo systemctl daemon-reload && sudo systemctl enable --now activemq.

[Unit]
Description=Apache ActiveMQ Classic
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
User=activemq
Group=activemq
Environment=JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ExecStart=/opt/activemq/bin/activemq start
ExecStop=/opt/activemq/bin/activemq stop
PIDFile=/opt/activemq/data/activemq.pid
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Pour les conteneurs, le projet publie une image officielle ; une seule ligne suffit pour obtenir un broker avec les mêmes valeurs par défaut que l'archive :

docker run -d --name activemq \
  -p 61616:61616 -p 8161:8161 -p 5672:5672 \
  -v activemq-data:/opt/apache-activemq/data \
  apache/activemq-classic:6.1.6

Montez aussi /opt/apache-activemq/conf si vous voulez fournir votre propre activemq.xml, et montez systématiquement le répertoire de données : sans volume, tous les messages persistants disparaissent à la suppression du conteneur.

Le contenu de la distribution

Connaître l'arborescence fait gagner du temps quand quelque chose tourne mal.

RépertoireContenu
bin/Le script de lancement activemq (Linux/macOS) et activemq.bat ; env contient les options de la JVM, dont la taille du heap
conf/activemq.xml (broker), jetty.xml et jetty-realm.properties (console web), login.config, users.properties, groups.properties (sécurité JAAS), log4j2.properties
data/Le store KahaDB, activemq.log, le fichier PID ; c'est le répertoire à sauvegarder et à placer sur un disque rapide
lib/Les jars du broker et du client ; activemq-all-*.jar regroupe tout pour des tests client rapides
webapps/La console d'administration web et l'endpoint REST Jolokia
examples/Des exemples de producteurs et de consommateurs pour chaque protocole

Le script de lancement propose bien plus que start et stop. bin/activemq status indique si le broker tourne ; bin/activemq bstat affiche les statistiques du broker ; dstat liste les destinations avec leur taille de file et leurs compteurs d'enqueue et de dequeue ; browse --amqurl tcp://localhost:61616 orders.placed affiche les messages d'une file sans les consommer ; purge en vide une ; producer et consumer génèrent et consomment du trafic de test. Ces commandes dialoguent avec le broker via JMX ou JMS : elles fonctionnent donc aussi contre des brokers distants.

La taille du heap se règle dans bin/env, via ACTIVEMQ_OPTS_MEMORY="-Xms64M -Xmx1G". Augmentez le maximum pour une vraie charge de travail, en gardant à l'esprit que la limite memoryUsage propre au broker (voir plus bas) n'est qu'une fraction de ce heap.

Lire activemq.xml

Le fichier de configuration est un contexte d'application Spring en XML, ce qui explique qu'il puisse définir des data sources et d'autres beans à côté du broker. Le broker lui-même correspond à l'élément <broker> ; ses enfants sont les sections que vous allez modifier.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://activemq.apache.org/schema/core
         http://activemq.apache.org/schema/core/activemq-core.xsd">

  <broker xmlns="http://activemq.apache.org/schema/core"
          brokerName="orders-broker"
          dataDirectory="${activemq.data}"
          useJmx="true"
          advisorySupport="false">   <!-- advisory topics cost CPU; enable only if you use them -->

    <destinationPolicy>
      <policyMap>
        <policyEntries>
          <!-- ">" matches every queue; "orders.>" would match orders.placed, orders.paid ... -->
          <policyEntry queue=">" producerFlowControl="true" memoryLimit="64mb">
            <deadLetterStrategy>
              <individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true"/>
            </deadLetterStrategy>
          </policyEntry>
          <policyEntry topic=">" producerFlowControl="true">
            <pendingMessageLimitStrategy>
              <constantPendingMessageLimitStrategy limit="1000"/>
            </pendingMessageLimitStrategy>
          </policyEntry>
        </policyEntries>
      </policyMap>
    </destinationPolicy>

    <persistenceAdapter>
      <kahaDB directory="${activemq.data}/kahadb"/>
    </persistenceAdapter>

    <systemUsage>
      <systemUsage sendFailIfNoSpace="true">
        <memoryUsage><memoryUsage percentOfJvmHeap="70"/></memoryUsage>
        <storeUsage><storeUsage limit="20 gb"/></storeUsage>
        <tempUsage><tempUsage limit="5 gb"/></tempUsage>
      </systemUsage>
    </systemUsage>

    <transportConnectors>
      <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
      <transportConnector name="amqp"     uri="amqp://0.0.0.0:5672?maximumConnections=1000"/>
      <transportConnector name="stomp"    uri="stomp://0.0.0.0:61613?maximumConnections=1000"/>
      <transportConnector name="mqtt"     uri="mqtt://0.0.0.0:1883?maximumConnections=1000"/>
      <transportConnector name="ws"       uri="ws://0.0.0.0:61614?maximumConnections=1000"/>
    </transportConnectors>
  </broker>

  <import resource="jetty.xml"/>   <!-- web console on 8161 -->
</beans>

Trois attributs de <broker> comptent dès le départ. brokerName identifie le broker dans JMX, dans les journaux et dans les réseaux de brokers — donnez à chacun un nom unique. dataDirectory désigne l'emplacement du store, du scheduler et du fichier de verrou. advisorySupport pilote les advisory messages, des topics internes sur lesquels le broker publie des événements comme « consommateur connecté » ou « message expiré » ; ils sont utiles à l'outillage mais ajoutent une surcharge, et beaucoup d'équipes les désactivent.

Politiques de destination

<destinationPolicy> applique des réglages par destination selon un motif de nom. Le joker > englobe tout ce qui se trouve sous un préfixe et * correspond à un seul segment de nom : orders.* couvre donc orders.placed mais pas orders.eu.placed. Les politiques peuvent fixer des limites mémoire, des stratégies de dead letter, le comportement du dispatch et, pour les topics, le nombre de messages qu'un abonné non durable trop lent peut accumuler avant que les plus anciens soient écartés — c'est le rôle du pendingMessageLimitStrategy ci-dessus, qui empêche un abonné bloqué d'épuiser la mémoire du broker.

L'individualDeadLetterStrategy présentée remplace la file unique et partagée ActiveMQ.DLQ par une file de dead letter par file source (DLQ.orders.placed), ce qui permet de remonter bien plus facilement à l'origine des poison messages.

Utilisation système et producer flow control

<systemUsage> fixe trois plafonds : memory pour les messages gardés en RAM, store pour le store persistant sur disque, et temp pour les messages non persistants déversés sur disque. Quand une destination ou le broker atteint un plafond, le producer flow control entre en jeu : le broker cesse d'acquitter les envois sur les destinations concernées, et les producteurs synchrones restent bloqués jusqu'à ce que des consommateurs libèrent de la place. Le broker est ainsi protégé contre le manque de mémoire, mais pour l'application émettrice, cela ressemble à un blocage pur et simple. sendFailIfNoSpace="true" transforme ce blocage en une ResourceAllocationException que le producteur peut traiter ; producerFlowControl="false" sur une politique désactive le mécanisme pour cette destination et laisse les messages déborder dans le store, ce qui est le choix habituel pour les files censées accumuler du retard.

Transport connectors et protocoles

Chaque <transportConnector> ouvre une socket d'écoute pour un protocole. Le schéma de l'URI détermine le protocole, la query string en règle le comportement.

SchémaProtocoleClients typiquesRemarques
tcp://OpenWire, le protocole binaire natif d'ActiveMQJava (activemq-client), .NET (NMS), C++ (CMS)Le plus rapide pour JMS ; un thread par connexion
nio://OpenWire sur Java NIOLes mêmesMoins de threads pour de nombreuses connexions ; à privilégier à grande échelle
ssl://, nio+ssl://OpenWire sur TLSLes mêmesConfigurez un keystore dans <sslContext>
amqp://AMQP 1.0Qpid JMS, Apache Qpid Proton, .NET, GoInteropérabilité fondée sur un standard
stomp://STOMPLangages de script, tests rapidesProtocole texte ; stomp+nio pour monter en charge
mqtt://MQTT 3.1.1Objets connectés, mobileLes QoS 0/1/2 correspondent aux modes de livraison
ws://, wss://STOMP ou MQTT sur WebSocketNavigateursÀ associer à un client STOMP JavaScript
vm://Dans la JVMTests, usage embarquéPas de socket ; le broker démarre à la première connexion

Parmi les options utiles figurent maximumConnections (refuser les nouvelles connexions au-delà d'une limite plutôt que d'épuiser les threads), wireFormat.maxFrameSize (rejeter les messages trop volumineux avant qu'ils n'atteignent la mémoire ; la valeur par défaut est de 100 Mo), transport.soTimeout et transport.keepAlive. N'écoutez sur 0.0.0.0 que si le broker doit être joignable depuis d'autres hôtes, et placez les listeners derrière un pare-feu : un broker exposé sur Internet avec les identifiants par défaut est un scénario d'incident bien connu.

URL côté client : failover et prefetch

Les clients se configurent en grande partie via l'URL de connexion, et deux options ont leur place dans toute URL de production.

Le failover transport encapsule une ou plusieurs adresses de broker et se reconnecte de manière transparente quand la connexion tombe, en rejouant les envois non acquittés et en rétablissant les consommateurs :

ConnectionFactory factory = new ActiveMQConnectionFactory(
    "failover:(tcp://broker-a:61616,tcp://broker-b:61616)"
  + "?randomize=false"                 // try broker-a first, broker-b as backup
  + "&initialReconnectDelay=100"
  + "&maxReconnectDelay=10000"         // back off to at most 10 s between attempts
  + "&maxReconnectAttempts=-1"         // keep trying forever (the default)
  + "&timeout=3000");                  // fail a send after 3 s without a broker instead of blocking

Même avec un seul broker, failover:(tcp://broker:61616) vaut la peine : sans lui, un redémarrage du broker laisse chaque client avec une connexion morte et une exception à gérer par lui-même.

Le prefetch est le nombre de messages que le broker pousse vers un consommateur avant acquittement. La valeur par défaut de 1000 pour les files maximise le débit des consommateurs rapides, mais ne convient pas aux consommateurs lents : l'un d'eux accapare un millier de messages pendant que ses neuf pairs restent inactifs, et un crash oblige à relivrer ces mille messages. Réglez-le par connexion, tcp://broker:61616?jms.prefetchPolicy.queuePrefetch=10, ou par destination, orders.placed?consumer.prefetchSize=1. Un prefetch de 1 garantit une équité stricte au prix d'un aller-retour par message ; une valeur de 10 à 50 est un compromis courant pour des traitements qui prennent quelques dizaines de millisecondes.

Brokers embarqués pour les tests

Le transport vm:// démarre un broker dans la JVM du premier client qui se connecte, ce qui rend les tests d'intégration autonomes :

// Fully in-memory: no KahaDB directory, no ports, gone when the JVM exits
ConnectionFactory factory =
    new ActiveMQConnectionFactory("vm://test?broker.persistent=false&broker.useJmx=false");

Le spring-boot-starter-activemq de Spring Boot embarquait autrefois un broker automatiquement ; depuis Boot 3.x, ce support a disparu. Pointez donc spring.activemq.broker-url vers une URL vm:// pour les tests ou, mieux, vers un conteneur démarré par Testcontainers, afin que les tests empruntent le vrai chemin réseau.

Réseaux de brokers

Un réseau de brokers relie plusieurs brokers pour qu'un message produit sur l'un puisse être consommé depuis un autre. Chaque broker conserve son propre store et ses propres clients ; un <networkConnector> ne transfère des messages vers un pair que si ce pair a un consommateur pour la destination : le réseau réagit à la demande au lieu de tout répliquer.

<networkConnectors>
  <!-- On broker-a: forward to broker-b when broker-b has interested consumers.
       duplex="true" carries traffic both ways over this single connector. -->
  <networkConnector name="a-to-b"
                    uri="static:(tcp://broker-b:61616)"
                    duplex="true"
                    networkTTL="1"
                    decreaseNetworkConsumerPriority="true">
    <excludedDestinations>
      <queue physicalName="internal.>"/>
    </excludedDestinations>
  </networkConnector>
</networkConnectors>

Les réseaux permettent de répartir les consommateurs et de relier des sites, mais ils n'apportent pas la haute disponibilité : un message stocké sur broker-a reste inaccessible tant que broker-a est arrêté. Dans Classic, la haute disponibilité passe par un store partagé (KahaDB sur stockage partagé ou store JDBC), avec un broker actif et des brokers de secours qui attendent le verrou ; le guide sur la persistance traite ce sujet. decreaseNetworkConsumerPriority="true" garde les messages en local quand un consommateur local existe, et networkTTL limite le nombre de sauts qu'un message peut effectuer — laissez-le à 1 sauf topologie multi-sauts délibérée, car les messages qui rebondissent entre brokers sont pénibles à déboguer.

Ce qui change avec Artemis

Si vous démarrez un nouveau déploiement, évaluez ActiveMQ Artemis avant de choisir Classic par défaut. Les différences qui touchent à la mise en place sont les suivantes :

  • La configuration se trouve dans etc/broker.xml, et artemis create génère un répertoire d'instance de broker distinct de l'installation.
  • Le modèle d'adresses repose sur des addresses, des queues et des routing types (anycast pour le point à point, multicast pour le pub-sub) au lieu de destinations JMS projetées directement sur des files et des topics.
  • La persistance s'appuie sur un journal append-only conçu sur mesure (avec un backend NIO ou Linux AIO) plutôt que sur KahaDB.
  • La haute disponibilité est intégrée : paires live/backup répliquées ou à store partagé, et connection routers.
  • AMQP 1.0 est un protocole de premier plan, avec sa propre fédération et son propre mirroring de broker à broker.
  • Le débit est généralement plus élevé, et WildFly comme le broker AMQ de Red Hat reposent sur lui.

Classic reste la bonne réponse quand votre outillage d'exploitation, vos politiques activemq.xml, vos clients OpenWire ou vos tableaux de bord JMX existent déjà, ou quand vous dépendez de fonctionnalités propres à Classic comme les virtual topics et les composite destinations. Les deux sont activement maintenus.

Erreurs fréquentes

  • Laisser admin/admin sur la console web. Modifiez conf/jetty-realm.properties avant que le broker soit joignable depuis autre chose que localhost, et réservez le port 8161 à un réseau d'exploitation.
  • Garder le heap par défaut sur une vraie charge. Le -Xmx de 1 Go de bin/env, combiné à percentOfJvmHeap="70", laisse peu de marge ; dimensionnez le heap sciemment et surveillez le pourcentage de mémoire utilisé.
  • Pas d'URL failover côté client. Un redémarrage du broker devient alors un incident applicatif. Encapsulez chaque URL dans failover:( ).
  • Un prefetch de 1000 avec des consommateurs lents. Les symptômes : un consommateur débordé, des pairs inactifs et de grosses rafales de relivraison après un crash. Baissez le prefetch.
  • Désactiver le producer flow control sans limites. Sans flow control ni limite de store, un producteur peut remplir le disque. Conservez storeUsage et mettez une alerte dessus.
  • Prendre un réseau de brokers pour de la réplication. C'est du routage, pas de la redondance. Utilisez un store partagé pour le failover.

Points clés à retenir

  • ActiveMQ Classic 6 exige Java 17, parle OpenWire, AMQP 1.0, STOMP, MQTT et WebSocket, et tourne à partir d'une archive, d'une unité systemd ou de l'image de conteneur officielle.
  • conf/activemq.xml est un fichier Spring XML ; les parties qui comptent sont <destinationPolicy>, <persistenceAdapter>, <systemUsage> et <transportConnectors>.
  • Les limites de mémoire, de stockage et d'espace temporaire déclenchent le producer flow control ; décidez destination par destination si les producteurs doivent se bloquer, échouer ou déborder sur disque.
  • La résilience des clients vient des URL failover:( ), et leur équité, d'un prefetch raisonnable — pas des réglages du broker.
  • Un réseau de brokers achemine les messages entre brokers à la demande ; la haute disponibilité exige un store partagé.
  • Artemis est l'autre broker du projet, avec une configuration et un modèle d'adresses différents ; préférez-le pour les nouveaux déploiements à fort débit ou en cluster, et Classic là où l'outillage et les fonctionnalités existants vous y retiennent.

Sources et lectures complémentaires

Questions fréquentes

Quelle version de Java faut-il pour ActiveMQ Classic 6 ?
Java 17 ou plus récent. La branche 6.x est passée en même temps à l'espace de noms jakarta.jms et à Java 17 ; elle va donc de pair avec les serveurs Jakarta EE 10 et Spring Boot 3.2+. Si vous êtes bloqué en Java 8 ou 11, ou avec des clients javax.jms, restez sur la branche 5.18 : elle est toujours maintenue, mais ne recevra plus de nouvelles fonctionnalités.
Quelle différence entre ActiveMQ Classic et ActiveMQ Artemis ?
Ce sont deux brokers distincts au sein d'un même projet Apache. Classic est la base de code d'origine, avec activemq.xml, KahaDB et OpenWire. Artemis descend de HornetQ : il utilise un autre modèle d'adresses, son propre journal, une configuration broker.xml, et traite AMQP 1.0 comme un protocole de premier plan ; c'est lui que WildFly embarque. Tous deux implémentent JMS. Classic est le choix prudent quand la configuration et l'outillage existent déjà ; Artemis est la meilleure plateforme pour les nouveaux déploiements à fort débit ou en cluster.
Pourquoi mon producteur reste-t-il bloqué sans jamais sortir de send() ?
C'est presque toujours le producer flow control. La destination ou le broker a atteint sa limite de mémoire, de stockage ou d'espace temporaire, et le broker a cessé d'acquitter les envois : un producteur synchrone reste donc bloqué. Repérez dans la console web les destinations au pourcentage de mémoire élevé, ajoutez des consommateurs ou relevez les limites, et activez sendFailIfNoSpace si vous préférez une exception à un blocage.
Peut-on exécuter ActiveMQ dans l'application plutôt que comme serveur séparé ?
Oui. Le transport vm:// démarre un broker embarqué dans la JVM à la première connexion ; ajoutez ?broker.persistent=false pour un broker entièrement en mémoire. C'est idéal pour les tests d'intégration et les petits outils mono-nœud, mais un broker de production partagé doit tourner dans son propre processus, avec son propre cycle de vie et sa propre supervision.