Mensageria e streaming de eventos
ActiveMQ Classic 6: instalação, activemq.xml e configuração de transportes
Intermediário10 min de leituraAtualizado
Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês
O ActiveMQ Classic é o veterano broker JMS do projeto Apache ActiveMQ. Este guia leva você de um arquivo recém-baixado a um broker que você entende: a estrutura de diretórios, as seções do activemq.xml que importam, os transport connectors e os protocolos, os limites de uso do sistema e o producer flow control, as URLs de failover no cliente, os brokers embarcados e as redes de brokers — apontando também onde o ActiveMQ Artemis é diferente.
O que você vai aprender
- Instalar e executar o ActiveMQ Classic 6 como serviço no Linux e como contêiner
- Ler e editar com segurança as principais seções do conf/activemq.xml
- Configurar transport connectors para clientes OpenWire, AMQP, STOMP, MQTT e WebSocket
- Definir os limites de memória, armazenamento e espaço temporário e entender o producer flow control
- Escrever URLs de conexão com failover e ajustar o prefetch dos clientes
- Conectar brokers em rede e saber quando o Artemis é a melhor escolha
O Apache ActiveMQ Classic é um broker JMS maduro escrito em Java: você baixa, inicia e, poucos segundos depois, tem filas, tópicos, um console web e listeners para cinco protocolos. A simplicidade é real, mas colocá-lo em produção depende de um punhado de decisões de configuração no conf/activemq.xml que os valores padrão não tomam por você. Este guia cobre a linha 6.x, que exige Java 17 e usa a API jakarta.jms, e aponta onde o ActiveMQ Artemis — o outro broker do projeto — faz as coisas de outro jeito.
Instalando o broker
O ActiveMQ é distribuído como um único arquivo compactado com o broker, suas bibliotecas e um console web. Não há instalador nem banco de dados para preparar.
Instale um JDK 17 ou mais recente. No Debian ou Ubuntu, sudo apt install openjdk-17-jre-headless; em sistemas da família RHEL, sudo dnf install java-17-openjdk-headless. Confira com java -version.
Crie um usuário de serviço. Rode o broker com uma conta sem privilégios: sudo useradd --system --home /opt/activemq --shell /usr/sbin/nologin activemq.
Baixe e descompacte. Baixe o tarball binário da página de downloads da Apache, verifique a assinatura ou o checksum, descompacte em /opt e mantenha um link simbólico independente da versão, para que uma atualização se resuma a trocar esse link:
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
Inicie uma vez em primeiro plano. /opt/activemq/bin/activemq console roda o broker preso ao seu terminal, para você acompanhar o log de inicialização. Você deve ver linhas anunciando o store KahaDB, cada transport connector (tcp://0.0.0.0:61616, amqp://... e assim por diante) e o console web em http://0.0.0.0:8161/. Pare com Ctrl+C.
Rode como serviço do systemd. Crie /etc/systemd/system/activemq.service e depois execute 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
Para contêineres, o projeto publica uma imagem oficial; uma única linha já entrega um broker com os mesmos padrões do tarball:
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
Monte também /opt/apache-activemq/conf se quiser fornecer seu próprio activemq.xml, e monte sempre o diretório de dados: sem um volume, todas as mensagens persistentes se perdem quando o contêiner é removido.
O que vem na distribuição
Conhecer a estrutura economiza tempo quando algo dá errado.
| Diretório | Conteúdo |
|---|---|
bin/ | O script de inicialização activemq (Linux/macOS) e activemq.bat; env guarda as opções da JVM, como o tamanho do heap |
conf/ | activemq.xml (broker), jetty.xml e jetty-realm.properties (console web), login.config, users.properties, groups.properties (segurança JAAS), log4j2.properties |
data/ | O store KahaDB, activemq.log, o arquivo de PID; é o diretório que você deve incluir no backup e colocar em disco rápido |
lib/ | Os jars do broker e do cliente; activemq-all-*.jar reúne tudo para testes rápidos de cliente |
webapps/ | O console web de administração e o endpoint REST do Jolokia |
examples/ | Producers e consumers de exemplo para cada protocolo |
O script de inicialização tem mais subcomandos do que start e stop. bin/activemq status informa se o broker está rodando; bin/activemq bstat mostra estatísticas do broker; dstat lista os destinos com o tamanho da fila e os contadores de enqueue e dequeue; browse --amqurl tcp://localhost:61616 orders.placed exibe as mensagens de uma fila sem consumi-las; purge esvazia uma fila; producer e consumer geram e consomem tráfego de teste. Esses comandos falam com o broker via JMX ou JMS, então também funcionam contra brokers remotos.
O tamanho do heap fica em bin/env, como ACTIVEMQ_OPTS_MEMORY="-Xms64M -Xmx1G". Aumente o máximo para cargas reais e lembre-se de que o limite memoryUsage do próprio broker (veja abaixo) é uma fração desse heap.
Lendo o activemq.xml
O arquivo de configuração é um application context do Spring em XML, e é por isso que ele pode definir data sources e outros beans ao lado do broker. O broker em si é o elemento <broker>; seus filhos são as seções que você vai editar.
<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&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>
Três atributos de <broker> importam logo de cara. brokerName identifica o broker no JMX, nos logs e nas redes de brokers — dê a cada broker um nome único. dataDirectory é onde ficam o store, o scheduler e o arquivo de lock. advisorySupport controla as advisory messages, tópicos internos nos quais o broker publica eventos como "consumer conectado" ou "mensagem expirada"; elas são úteis para ferramentas, mas adicionam overhead, e muitas equipes as desativam.
Políticas de destino
<destinationPolicy> aplica configurações por destino de acordo com um padrão de nome. O curinga > abrange tudo abaixo de um prefixo e * corresponde a um único segmento do nome, então orders.* cobre orders.placed, mas não orders.eu.placed. As políticas podem definir limites de memória, estratégias de dead letter, o comportamento do dispatch e, para tópicos, quantas mensagens um assinante não durável lento pode acumular antes que as mais antigas sejam descartadas — é o pendingMessageLimitStrategy acima, que impede um assinante travado de esgotar a memória do broker.
A individualDeadLetterStrategy mostrada substitui a fila única e compartilhada ActiveMQ.DLQ por uma fila de dead letter para cada fila de origem (DLQ.orders.placed), o que torna muito mais fácil rastrear a origem das poison messages.
Uso do sistema e producer flow control
<systemUsage> define três tetos: memory para as mensagens mantidas em RAM, store para o store persistente em disco e temp para as mensagens não persistentes despejadas em disco. Quando um destino ou o broker atinge um teto, o producer flow control entra em ação: o broker para de confirmar os envios nos destinos afetados, e os producers síncronos ficam bloqueados até que os consumers liberem espaço. Isso evita que o broker fique sem memória, mas, para a aplicação que envia, parece um travamento. sendFailIfNoSpace="true" transforma esse travamento em uma ResourceAllocationException que o producer pode tratar; producerFlowControl="false" em uma política desativa o mecanismo para aquele destino e deixa as mensagens transbordarem para o store, que é a escolha usual para filas que devem acumular backlog.
Transport connectors e protocolos
Cada <transportConnector> abre um socket de escuta para um protocolo. O esquema da URI seleciona o protocolo e a query string o ajusta.
| Esquema | Protocolo | Clientes típicos | Observações |
|---|---|---|---|
tcp:// | OpenWire, o protocolo binário nativo do ActiveMQ | Java (activemq-client), .NET (NMS), C++ (CMS) | O mais rápido para JMS; uma thread por conexão |
nio:// | OpenWire sobre Java NIO | Os mesmos | Menos threads para muitas conexões; prefira em escala |
ssl://, nio+ssl:// | OpenWire sobre TLS | Os mesmos | Configure uma keystore em <sslContext> |
amqp:// | AMQP 1.0 | Qpid JMS, Apache Qpid Proton, .NET, Go | Interoperabilidade baseada em padrões |
stomp:// | STOMP | Linguagens de script, testes rápidos | Protocolo de texto; stomp+nio para escalar |
mqtt:// | MQTT 3.1.1 | Dispositivos IoT, mobile | Os QoS 0/1/2 correspondem aos modos de entrega |
ws://, wss:// | STOMP ou MQTT sobre WebSocket | Navegadores | Use com um cliente STOMP em JavaScript |
vm:// | Dentro da JVM | Testes, uso embarcado | Sem socket; o broker sobe na primeira conexão |
Entre as opções úteis estão maximumConnections (recusar novas conexões acima de um limite em vez de esgotar as threads), wireFormat.maxFrameSize (rejeitar mensagens grandes demais antes que cheguem à memória; o padrão é 100 MB), transport.soTimeout e transport.keepAlive. Faça bind em 0.0.0.0 só quando o broker precisar ser acessível de outros hosts, e coloque os listeners atrás de um firewall — um broker exposto à internet com as credenciais padrão é um padrão de incidente bem conhecido.
URLs do cliente: failover e prefetch
Os clientes configuram muita coisa pela URL de conexão, e duas opções devem estar em toda URL de produção.
O failover transport envolve um ou mais endereços de broker e reconecta de forma transparente quando a conexão cai, reenviando os envios não confirmados e restabelecendo os consumers:
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
Mesmo com um único broker, failover:(tcp://broker:61616) vale a pena: sem ele, um restart do broker deixa cada cliente com uma conexão morta e uma exceção que ele mesmo precisa tratar.
O prefetch é o número de mensagens que o broker empurra para um consumer antes da confirmação. O padrão de 1000 para filas maximiza o throughput com consumers rápidos, mas é inadequado para os lentos: um consumer monopoliza mil mensagens enquanto seus nove pares ficam ociosos, e uma queda obriga a reentregar essas mil. Ajuste por conexão, tcp://broker:61616?jms.prefetchPolicy.queuePrefetch=10, ou por destino, orders.placed?consumer.prefetchSize=1. Um prefetch de 1 garante uma distribuição estritamente justa ao custo de uma ida e volta por mensagem; de 10 a 50 é um meio-termo comum para processamentos que levam dezenas de milissegundos.
Brokers embarcados para testes
O transporte vm:// sobe um broker dentro da JVM do primeiro cliente que se conecta, o que torna os testes de integração autossuficientes:
// 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");
O spring-boot-starter-activemq do Spring Boot costumava embarcar um broker automaticamente; desde o Boot 3.x esse suporte não existe mais, então aponte spring.activemq.broker-url para uma URL vm:// nos testes ou, melhor ainda, para um contêiner iniciado pelo Testcontainers, para que os testes exercitem o caminho de rede real.
Redes de brokers
Uma rede de brokers conecta vários brokers para que uma mensagem produzida em um possa ser consumida a partir de outro. Cada broker mantém seu próprio store e seus próprios clientes; um <networkConnector> só encaminha mensagens para um par quando esse par tem um consumer para o destino, de modo que a rede reage à demanda em vez de replicar tudo.
<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>
Redes servem para escalar consumers e conectar localidades, mas não oferecem alta disponibilidade: uma mensagem armazenada no broker-a fica indisponível enquanto o broker-a estiver fora do ar. No Classic, a alta disponibilidade vem de um store compartilhado (KahaDB em armazenamento compartilhado ou um store JDBC), com um broker ativo e brokers em standby aguardando o lock, como mostra o guia de persistência. decreaseNetworkConsumerPriority="true" mantém as mensagens locais quando existe um consumer local, e networkTTL limita quantos saltos uma mensagem pode dar — deixe em 1, a menos que você tenha uma topologia multi-hop deliberada, porque mensagens que ficam quicando entre brokers são um pesadelo para depurar.
Onde o Artemis é diferente
Se você está começando uma implantação nova, avalie o ActiveMQ Artemis antes de adotar o Classic por padrão. As diferenças que afetam a configuração inicial são:
- A configuração fica em
etc/broker.xml, eartemis creategera um diretório de instância do broker separado da instalação. - O modelo de endereços tem addresses, queues e routing types (anycast para ponto a ponto, multicast para pub-sub) em vez de destinos JMS mapeados diretamente em filas e tópicos.
- A persistência usa um journal append-only feito sob medida (com backend NIO ou Linux AIO) em vez do KahaDB.
- A alta disponibilidade é nativa: pares live/backup replicados ou com store compartilhado, e connection routers.
- O AMQP 1.0 é um protocolo de primeira classe, com federação e mirroring próprios entre brokers.
- O throughput costuma ser maior, e tanto o WildFly quanto o broker AMQ da Red Hat são baseados nele.
O Classic continua sendo a resposta certa quando suas ferramentas de operação, políticas no activemq.xml, clientes OpenWire ou dashboards JMX já existem, ou quando você depende de recursos exclusivos do Classic, como virtual topics e composite destinations. Os dois são mantidos ativamente.
Erros comuns
- Deixar
admin/adminno console web. Altereconf/jetty-realm.propertiesantes que o broker fique acessível de qualquer lugar além de localhost, e restrinja a porta 8161 a uma rede de operações. - Rodar com o heap padrão em uma carga real. O
-Xmxde 1 GB dobin/env, combinado compercentOfJvmHeap="70", deixa pouca folga; dimensione o heap de forma deliberada e monitore o percentual de memória usado. - Nenhuma URL de failover nos clientes. Um restart do broker vira um incidente da aplicação. Envolva toda URL em
failover:( ). - Prefetch de 1000 com consumers lentos. Os sintomas são um consumer sobrecarregado e pares ociosos, além de grandes rajadas de reentrega depois de uma queda. Reduza o prefetch.
- Desativar o producer flow control sem limites. Sem flow control e sem limite de store, um producer pode encher o disco. Mantenha o
storeUsagee configure alertas sobre ele. - Tratar uma rede de brokers como replicação. É roteamento, não redundância. Use um store compartilhado para failover.
Principais pontos
- O ActiveMQ Classic 6 exige Java 17, fala OpenWire, AMQP 1.0, STOMP, MQTT e WebSocket, e roda a partir de um tarball, de uma unit do systemd ou da imagem de contêiner oficial.
conf/activemq.xmlé um arquivo Spring XML; as partes que importam são<destinationPolicy>,<persistenceAdapter>,<systemUsage>e<transportConnectors>.- Os limites de memória, armazenamento e espaço temporário disparam o producer flow control; decida, destino a destino, se os producers devem bloquear, falhar ou transbordar para o disco.
- A resiliência dos clientes vem das URLs
failover:( ), e a distribuição justa, de um prefetch sensato — não das configurações do broker. - Uma rede de brokers roteia mensagens entre brokers sob demanda; alta disponibilidade exige um store compartilhado.
- O Artemis é o outro broker do projeto, com configuração e modelo de endereços diferentes; prefira-o para implantações novas de alto throughput ou em cluster, e o Classic quando as ferramentas e os recursos existentes prendem você a ele.
Fontes e leituras complementares
Perguntas frequentes
- Qual versão do Java o ActiveMQ Classic 6 exige?
- Java 17 ou mais recente. A linha 6.x migrou ao mesmo tempo para o namespace jakarta.jms e para o Java 17, então combina com servidores Jakarta EE 10 e com o Spring Boot 3.2+. Se você está preso ao Java 8 ou 11 ou a clientes javax.jms, fique na linha 5.18: ela continua mantida, mas não vai receber funcionalidades novas.
- Qual é a diferença entre o ActiveMQ Classic e o ActiveMQ Artemis?
- São dois brokers distintos dentro do mesmo projeto Apache. O Classic é a base de código original, com activemq.xml, KahaDB e OpenWire. O Artemis descende do HornetQ: usa outro modelo de endereços, um journal próprio e uma configuração broker.xml, e trata o AMQP 1.0 como protocolo de primeira classe; é ele que o WildFly embarca. Os dois implementam JMS. O Classic é a escolha segura quando você já tem configuração e ferramentas; o Artemis é a plataforma mais indicada para implantações novas, de alto throughput ou em cluster.
- Por que meu producer trava e nunca retorna de send()?
- Quase sempre é o producer flow control. O destino ou o broker atingiu seu limite de memória, armazenamento ou espaço temporário e o broker parou de confirmar os envios, então um producer síncrono fica bloqueado. Procure no console web os destinos com percentual de memória alto, adicione consumers ou aumente os limites, e ative sendFailIfNoSpace se preferir uma exceção a um travamento.
- Posso rodar o ActiveMQ dentro da minha aplicação em vez de como servidor separado?
- Sim. O transporte vm:// sobe um broker embarcado dentro da JVM na primeira conexão; adicione ?broker.persistent=false para ter um broker puramente em memória. É ideal para testes de integração e pequenas ferramentas de nó único, mas um broker de produção compartilhado deve rodar em processo próprio, com ciclo de vida e monitoramento próprios.