安裝ActiveMQ/隊列消息/發布訂閱

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容