1.下載ActiveMQ,http://activemq.apache.org/
2.解壓后結構如下
bin存放的是腳本文件
conf存放的是基本配置文件
data存放的是日志文件
docs存放的是說明文檔
examples存放的是簡單的實例
lib存放的是activemq所需jar包
webapps用于存放項目的目錄
3.啟動MQ
- 進入bin目錄,./activemq start(linux)
- 進入bin目錄,執行activemq start(windows)
啟動后打開http://127.0.0.1:8161/admin/,可以看到MQ的控制臺,用戶名密碼默認都是admin。
4.更改用戶名密碼
activemq.xml中有<import resource="jetty.xml"/>, jetty.xml 中有
<bean id="securityLoginService" class="org.eclipse.jetty.security.HashLoginService">
<property name="name" value="ActiveMQRealm" />
<property name="config" value="${activemq.conf}/jetty-realm.properties" />
</bean>
在jetty-realm.properties中可更改用戶名密碼。
示例
添加maven依賴
<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-all -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.13.3</version>
</dependency>
P2P生產者:參考
package com.tgb.activemq;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 消息的生產者(發送者)
*
*/
public class JMSProducer {
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;//默認連接用戶名
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;//默認連接密碼
private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;//默認連接地址
private static final int SENDNUM = 10;//發送的消息數量
public static void main(String[] args) {
ConnectionFactory connectionFactory;//連接工廠
Connection connection = null;//連接
Session session;//會話 接受或者發送消息的線程
Destination destination;//消息的目的地
MessageProducer messageProducer;//消息生產者
connectionFactory = new ActiveMQConnectionFactory(JMSProducer.USERNAME, JMSProducer.PASSWORD, JMSProducer.BROKEURL);//實例化連接工廠
try {
connection = connectionFactory.createConnection();//通過連接工廠獲取連接
connection.start();//啟動連接
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);//創建session
destination = session.createQueue("HelloWorld");//創建一個名稱為HelloWorld的消息隊列
messageProducer = session.createProducer(destination);//創建消息生產者
sendMessage(session, messageProducer);//發送消息
session.commit();
} catch (Exception e) {
e.printStackTrace();
}finally{
if(connection != null){
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
/**
* 發送消息
* @param session
* @param messageProducer 消息生產者
* @throws Exception
*/
public static void sendMessage(Session session,MessageProducer messageProducer) throws Exception{
for (int i = 0; i < JMSProducer.SENDNUM; i++) {
TextMessage message = session.createTextMessage("ActiveMQ 發送消息" +i);//創建一條文本消息
System.out.println("發送消息:Activemq 發送消息" + i);
messageProducer.send(message);//通過消息生產者發出消息
}
}
}
P2P消費者
package testactivemq;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* Created by zzhblh on 2016/8/27.
*/
public class JMSConsumer {
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;//默認連接用戶名
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;//默認連接密碼
private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;//默認連接地址
public static void main(String[] args) {
ConnectionFactory connectionFactory;//連接工廠
Connection connection = null;//連接
Session session;//會話 接受或者發送消息的線程
Destination destination;//消息的目的地
MessageConsumer messageConsumer;//消息的消費者
connectionFactory = new ActiveMQConnectionFactory(JMSConsumer.USERNAME, JMSConsumer.PASSWORD, JMSConsumer.BROKEURL);//實例化連接工廠
try {
connection = connectionFactory.createConnection();//通過連接工廠獲取連接
connection.start();//啟動連接
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//創建session
destination = session.createQueue("HelloWorld");//創建一個連接到HelloWorld的消息隊列
messageConsumer = session.createConsumer(destination);//創建消息消費者
//只能收到一個
TextMessage textMessage = (TextMessage) messageConsumer.receive(100000);//使這個messageConsumer運行500秒
if(textMessage != null){
System.out.println("收到的消息:" + textMessage.getText());
}
//收到所有
messageConsumer.setMessageListener(new Listener());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
Topic 生產者
package testactivemq;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQMapMessage;
import javax.jms.*;
/**
* Created by zzhblh on 2016/8/27.
*/
public class Publisher {
ConnectionFactory factory;//連接工廠
Connection connection = null;//連接
Session session;//會話 接受或者發送消息的線程
Destination[] destinations;//消息的目的地
MessageProducer producer;
public Publisher() throws JMSException {
factory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, ActiveMQConnection.DEFAULT_BROKER_URL);
connection = factory.createConnection();
try {
connection.start();//啟動連接
} catch (JMSException e) {
connection.close();
throw e;
}
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(null);
}
protected void setTopics(String[] stocks) throws JMSException {
destinations = new Destination[stocks.length];
for(int i = 0; i < stocks.length; i++) {
destinations[i] = session.createTopic("STOCKS." + stocks[i]);
}
}
protected void sendMessage(String[] stocks) throws JMSException {
for(int i = 0; i < stocks.length; i++) {
Message message = createStockMessage(stocks[i], session);
System.out.println("Sending: " + ((ActiveMQMapMessage)message).getContentMap() + " on destination: " + destinations[i]);
producer.send(destinations[i], message);
}
}
protected Message createStockMessage(String stock, Session session) throws JMSException {
MapMessage message = session.createMapMessage();
message.setString("stock", stock);
message.setDouble("price", 1.00);
message.setDouble("offer", 0.01);
message.setBoolean("up", true);
return message;
}
public void close() throws JMSException {
if (connection != null) {
connection.close();
}
}
public static void main(String[] args) throws JMSException {
// Create publisher
Publisher publisher = new Publisher();
// Set topics
String[] stocks = {"a","b","c","d","e"};
publisher.setTopics(stocks);
for(int i = 0; i < 10; i++) {
publisher.sendMessage(stocks);
System.out.println("Publisher '" + i + " price messages");
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
// Close all resources
publisher.close();
}
}
Topic消費者
package testactivemq;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.text.DecimalFormat;
/**
* Created by zzhblh on 2016/8/27.
*/
public class Subscriber {
ConnectionFactory factory;//連接工廠
Connection connection = null;//連接
Session session;//會話 接受或者發送消息的線程
Destination destination;//消息的目的地
public Subscriber() throws JMSException {
factory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, ActiveMQConnection.DEFAULT_BROKER_URL);
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
public static void main(String[] args) throws JMSException {
Subscriber consumer = new Subscriber();
String[] stocks = {"a","b","c","d","e"};//找到發布的Topic
for (String stock : stocks) {
Destination destination = consumer.getSession().createTopic("STOCKS." + stock);
MessageConsumer messageConsumer = consumer.getSession().createConsumer(destination);
//只能收到一次
MapMessage map = (MapMessage) messageConsumer.receive();//使這個messageConsumer運行500秒
if(map != null){
stock = map.getString("stock");
double price = map.getDouble("price");
double offer = map.getDouble("offer");
boolean up = map.getBoolean("up");
DecimalFormat df = new DecimalFormat("#,###,###,##0.00");
System.out.println(stock + "\t" + df.format(price) + "\t" + df.format(offer) + "\t" + (up ? "up" : "down"));
}else {
break;
}
//監聽收到所有
messageConsumer.setMessageListener(new Listener());
}
}
public Session getSession() {
return session;
}
}
用于監聽的Listener
package testactivemq;
import org.apache.activemq.command.ActiveMQTextMessage;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import java.text.DecimalFormat;
/**
* Created by zzhblh on 2016/8/27.
*/
public class Listener implements MessageListener {
public void onMessage(Message message) {
try {
if(message instanceof ActiveMQTextMessage){
ActiveMQTextMessage textMessage = (ActiveMQTextMessage) message;
System.out.println("收到的消息:" + textMessage.getText());
}
if(message instanceof MapMessage){
MapMessage map = (MapMessage) message;
String stock = map.getString("stock");
double price = map.getDouble("price");
double offer = map.getDouble("offer");
boolean up = map.getBoolean("up");
DecimalFormat df = new DecimalFormat("#,###,###,##0.00");
System.out.println(stock + "\t" + df.format(price) + "\t" + df.format(offer) + "\t" + (up ? "up" : "down"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
異步與同步接收
- 消息的異步接收:
異步接收是指當消息到達時,主動通知客戶端。通過客戶為消費者注冊一個消息監聽器,以定義在消息到達時所采取的動作。
JMS客戶端可以通過注冊一個實現MessageListener接口的對象到MessageConsumer,這樣,每當消息到達時,JMS Provider 會調用MessageListener中的onMessage 方法。會話(主題或隊列)負責產生某些消息,這些消息被傳送到使用onMessage方法的監聽者那里。 - 消息的同步接收:
jms同步接受消息的功能(客戶端必須請求每個消息),通過調用消費者的receive方法從目的地中顯式提取消息,receive方法可以一直阻塞到消息到達。
接收消息的方法還有一個"不等待"的版本,使用這個方法時QueueReceiver對象檢查是否有消息之后立即返回,將控制交還給程序。
TextMessage message = queueReceiver.receiveNoWait ()。
jms消息的確認模式
一般建議,一個事務類型的Session中只有一個Consumer,避免混亂。
public Consumer() throws JMSException {
factory = new ActiveMQConnectionFactory(brokerURL);
connection = factory.createConnection();
connection.start();
//第一個參數:是否支持事務,如果為true,則會忽略第二個參數,被jms服務器設置為SESSION_TRANSACTED
//第一個參數為false時,第二個參數的值可為Session.AUTO_ACKNOWLEDGE,Session.CLIENT_ACKNOWLEDGE,DUPS_OK_ACKNOWLEDGE其中一個。
//Session.AUTO_ACKNOWLEDGE為自動確認,在同步模式下(使用consumer.receive):客戶端發送和接收消息不需要做額外的工作。哪怕是接收端發生異常,也會被當作正常發送成功,此消息將丟失。基于異步調用時(使用Listener),消息的確認是在onMessage方法返回之后,如果onMessage方法異常,會導致消息不能被ACK,會觸發重發。
// 對于consumer而言,optimizeAcknowledge屬性只會在AUTO_ACK模式下有效。
//Session.CLIENT_ACKNOWLEDGE為客戶端確認。客戶端接收到消息后,必須調用javax.jms.Message的acknowledge方法。jms服務器才會當作發送成功,并刪除消息。需要注意的是調用acknowledge方法后,此session下所有未確認的消息將全部被確認,一般建議,一個事務類型的Session中只有一個Consumer,避免混亂。
//DUPS_OK_ACKNOWLEDGE允許副本的確認模式。一旦接收方應用程序的方法調用從處理消息處返回,會話對象就會確認消息的接收;而且允許重復確認。
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
如果使用CLIENT_ACKNOWLEDGE,則需要手動確認。否則MQ-SERVER不會刪除消息,消息會被重復發送。
msg = (TextMessage) consumer.receive();
//acknowledge
msg.acknowledge();
如果第一個參數為true,則一定要session.commit()。否則MQ-SERVER不會刪除消息,消息會被重復發送。當session使用事務時,在事務開啟之后,和session.commit()之前,所有消費的消息,要么全部正常確認,要么全部redelivery。
session.commit();
messageConsumer.close();
session.close();
connection.close();
Broker內每條消息都有一個ACK_TYPE,它通常是一種內部機制,并不會面向開發者。ActiveMQ中定義了如下幾種ACK_TYPE(參看MessageAck類):
- DELIVERED_ACK_TYPE = 0 消息"已接收",但尚未處理結束
- STANDARD_ACK_TYPE = 2 "標準"類型,通常表示為消息"處理成功",broker端可以刪除消息了
- POSION_ACK_TYPE = 1 消息"錯誤",通常表示"拋棄"此消息,比如消息重發多次后,都無法正確處理時,消息將會被刪除或者DLQ(死信隊列)
- REDELIVERED_ACK_TYPE = 3 消息需"重發",比如consumer處理消息時拋出了異常,broker稍后會重新發送此消息
- INDIVIDUAL_ACK_TYPE = 4 表示只確認"單條消息",無論在任何ACK_MODE下
- UNMATCHED_ACK_TYPE = 5 在Topic中,如果一條消息在轉發給“訂閱者”時,發現此消息不符合Selector過濾條件,那么此消息將 不會轉發給訂閱者,消息將會被存儲引擎刪除(相當于在Broker上確認了消息)。
要點:
1.connection = connectionFactory.createConnection();//通過連接工廠獲取連接
2.connection.start();//啟動連接
3.session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);//創建session,true表示是否事務
4.destination = session.createQueue("HelloWorld");//創建一個名稱為HelloWorld的消息隊列
5.messageProducer = session.createProducer(destination);//創建消息生產者
6.TextMessage message = session.createTextMessage("ActiveMQ 發送消息")//創建消息
7.messageProducer.send(message);//通過消息生產者發出消息
8.connection.close();//關閉connection
參考:
http://shift-alt-ctrl.iteye.com/blog/2034440
http://shift-alt-ctrl.iteye.com/blog/2020182