Apache ActiveMQ 是最成熟、功能最全的開源消息中間件之一:
apache-activemq-5.17.0/
├── bin/ # activemq 脚本、activemq.bat
├── conf/ # activemq.xml、jetty.xml、log4j2.properties、users.properties
├── data/ # KahaDB 持久化文件、日誌
├── docs/ # 文檔
├── examples/ # 示例代碼
├── lib/ # 核心 jar、依賴 jar
├── webapps/ # admin console (hawtio)、demo
└── webapps-demo/ # 額外示例
# 前台運行(調試用)
bin/activemq console
# 後台運行(生產用)
bin/activemq start
# 指定配置文件
bin/activemq console xbean:file:/opt/activemq/conf/activemq.xml
成功啟動後訪問:
https://localhost:8161/admin/ (默認 admin/admin)service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmitcp://localhost:61616| 現象 | 原因 | 解決 |
|---|---|---|
| JVM_Bind 61616 端口被佔用 | Windows ICS 服務或其他進程佔用 | 停用 ICS / 修改 conf/activemq.xml <transportConnector port="61617"/> | JDK 版本過低 | 升級 JDK 1.8+ |
| 內存不足 | 默認堆 512M 不夠 | 修改 bin/activemq ACTIVEMQ_OPTS_MEMORY="-Xms1g -Xmx2g" |
| 領域 | JMS 統一 | Point-to-Point (Queue) | Pub/Sub (Topic) |
|---|---|---|---|
| ConnectionFactory | ConnectionFactory | QueueConnectionFactory | TopicConnectionFactory |
| Connection | Connection | QueueConnection | TopicConnection |
| Destination | Destination | Queue | Topic |
| Session | Session | QueueSession | TopicSession |
| Producer | MessageProducer | QueueSender | TopicPublisher |
| Consumer | MessageConsumer | QueueReceiver | TopicSubscriber |
現代代碼請統一使用 JMS 1.1 統一接口(ConnectionFactory、Destination、Session、MessageProducer/Consumer),避免耦合特定域接口。
consumer.receive() 同步拉取 / setMessageListener() 異步回調<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>5.17.0</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.17.0</version>
</dependency>
public class JmsProducer {
private static final String BROKER_URL = "tcp://localhost:61616";
private static final String DESTINATION = "demo.queue"; // 或 demo.topic
public static void main(String[] args) throws JMSException {
// 1. 創建連接工廠(生產環境用 PooledConnectionFactory)
ConnectionFactory factory = new ActiveMQConnectionFactory(BROKER_URL);
// 生產環境建議:
// PooledConnectionFactory pooled = new PooledConnectionFactory(factory);
// pooled.setMaxConnections(100);
try (Connection conn = factory.createConnection();
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE)) {
conn.start();
Destination dest = session.createQueue(DESTINATION); // 或 createTopic
MessageProducer producer = session.createProducer(dest);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); // 或 PERSISTENT
for (int i = 1; i <= 5; i++) {
TextMessage msg = session.createTextMessage("Message #" + i);
producer.send(msg);
System.out.println("Sent: " + msg.getText());
}
}
}
}
// 同步拉取(簡單、阻塞)
public class SyncConsumer {
public static void main(String[] args) throws JMSException {
ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
try (Connection conn = factory.createConnection();
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE)) {
conn.start();
MessageConsumer consumer = session.createConsumer(session.createQueue("demo.queue"));
while (true) {
Message msg = consumer.receive(5000); // 5s 超時
if (msg instanceof TextMessage) System.out.println("Received: " + ((TextMessage)msg).getText());
else break;
}
}
}
}
// 異步監聽(高併發、事件驅動)
public class AsyncConsumer implements MessageListener {
public static void main(String[] args) throws JMSException {
ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection conn = factory.createConnection();
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
conn.start();
MessageConsumer consumer = session.createConsumer(session.createQueue("demo.queue"));
consumer.setMessageListener(new AsyncConsumer()); // 註冊回調
// 主線程保持存活
new CountDownLatch(1).await();
}
@Override public void onMessage(Message msg) {
try { if (msg instanceof TextMessage) System.out.println("Async: " + ((TextMessage)msg).getText()); }
catch (JMSException e) { e.printStackTrace(); }
}
}
若維護舊系統需使用 QueueConnectionFactory 等域接口,參考原文代碼結構,核心差異僅在創建對象類型:
QueueConnectionFactory → QueueConnection → QueueSession → QueueSender/QueueReceiverTopicConnectionFactory → TopicConnection → TopicSession → TopicPublisher/TopicSubscriber建議:新代碼統一遷移至 JMS 1.1 統一 API。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>5.3.x</version> </dependency>
<dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-pool</artifactId> <version>5.17.0</version> </dependency>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1. 連接池工廠(生產必備) -->
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
</property>
<property name="maxConnections" value="100"/>
</bean>
<!-- 2. 目標 Queue/Topic -->
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="spring.demo.queue"/>
</bean>
<!-- 3. JmsTemplate -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="pooledConnectionFactory"/>
<property name="defaultDestination" ref="destination"/>
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter"/>
</property>
</bean>
<!-- 4. 異步監聽容器(替代手動 setMessageListener) -->
<bean id="messageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="pooledConnectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="myListener"/>
<property name="concurrentConsumers" value="5"/>
<property name="maxConcurrentConsumers" value="20"/>
</bean>
<bean id="myListener" class="com.example.MyMessageListener"/>
</beans>
// 發送
@Component
public class SpringSender {
@Autowired private JmsTemplate jmsTemplate;
public void send(String text) {
jmsTemplate.send(session -> session.createTextMessage(text));
}
}
// 接收(實現 MessageListener)
@Component
public class MyMessageListener implements MessageListener {
@Override public void onMessage(Message msg) {
if (msg instanceof TextMessage) {
try { System.out.println("Spring Received: " + ((TextMessage)msg).getText()); }
catch (JMSException e) { throw new RuntimeException(e); }
}
}
}
| 維度 | 建議配置 | 說明 |
|---|---|---|
| 持久化存儲 | KahaDB (默認) / LevelDB / JDBC | KahaDB 適合大多數場景;極高吞吐考慮 LevelDB;需共享存儲用 JDBC |
| 連接工廠 | 必須用 PooledConnectionFactory | 避免頻繁創建 TCP 連接,設置 maxConnections=100+ |
| 生產者確認 | 持久化消息 + sendTimeout / ProducerWindowSize | 防止生產者阻塞導致 OOM |
| 消費者預取 | prefetchPolicy.queuePrefetch=1000 (默認) | 批量拉取減少網絡交互;低延遲場景可調小 |
| 異步發送 | useAsyncSend=true (非持久化) | 非持久化消息異步發送,吞吐提升 3-5 倍 |
| 流控 | producerFlowControl=true, memoryLimit | 防止生產過快撐爆 Broker 內存 |
| 高可用 | Master/Slave (共享 KahaDB/LevelDB) + Network of Brokers | 自動故障轉移,跨機房部署 |
| 監控 | JMX + Prometheus JMX Exporter + Grafana | 關鍵指標:Enqueue/Dequeue Rate, Queue Size, Memory %, DLQ Size |
| 死信隊列 | deadLetterStrategy 共享 DLQ 或每 Queue 獨立 DLQ | 便於排查重試耗盡消息 |
conn.start():Connection 默認不啟動消息分發,消費者收不到消息。commit():createSession(true, ...) 時必須顯式提交,否則消息不入隊/不確認。DurableSubscriber:session.createDurableSubscriber(topic, "subName"),否則離線消息丟失。ObjectMessage 反序列化風險:ActiveMQ 5.13+ 默認拒絕非白名單類,配置 trustAllPackages=true 或精確白名單。總結:ActiveMQ 憑藉成熟的 JMS 實現、豐富的協議支持、靈活的部署拓撲與 Spring 生態無縫整合,仍是中小型項目、傳統企業應用、混合語言系統的首選消息中間件。掌握「統一 API、連接池、持久化策略、預取調優、高可用架構」五大核心,即可應對 90% 生產場景。