[中間件] 消息處理利器 ActiveMQ 的介紹 & Stomp 協議的使用

隨着互聯網企業業務量的不斷擴大,企業信息網絡系統的愈加複雜,性能問題也就越來越凸顯出來,串行的業務處理方式顯然已經成爲主要的瓶頸,我們需要更多異步的並行處理來提高企業信息系統的業務處理能力,因此獨立的消息處理系統也就應運而生,ActiveMQ 就是諸多開源消息系統的佼佼者。對於我們的技術選型來說,穩定和適應性是最重要的考慮因素,因此由 Apache 組織背景而且支持發佈/訂閱(Pub/Sub)模式以及異步 Stomp 協議(Streaming Text Orientated Messaging Protocol)和 REST 方式的 ActiveMQ 就成爲了首選的消息處理器,經測試在異步模式下,整個系統的信息處理吞吐量還是很理想的(一臺普通服務器能達到每秒收發 4000 個消息,每個消息 4K 字節這樣的速度)。

下面我們來看看 ActiveMQ 的基本配置與使用,由於我們公司大部分的內部服務都是使用腳本語言寫的,比如 PHP/Python 等,所以我們使用 Stomp 協議來處理;另外,爲了儘可能提高消息“生產者”的效率,我們採用了異步 nio 模式;還有關於併發處理以及負載均衡方面還有很多需要注意的配置和選項,我們後面會另找時間補充。

1、下載 ActiveMQ(最新版本 5.4.1)

2、解壓安裝到 /usr/local/activemq 目錄下

3、安裝初始化配置文件 ./bin/activemq setup /etc/default/activemq (保存至默認位置,以後可微調啓動參數)

4、編輯 ./conf/activemq.xml 加入如下段(authentication / stomp+nio):
...
<plugins>
<!-- Add By James ; Configure authentication; Username, passwords and groups -->
    <simpleAuthenticationPlugin>
        <users>
            <authenticationUser username="system" password="${activemq.password}" groups="users,admins"/>
            <authenticationUser username="user" password="${guest.password}" groups="users"/>
            <authenticationUser username="guest" password="${guest.password}" groups="guests"/>
        </users>
    </simpleAuthenticationPlugin>
</plugins>
...
<!-- Add by James for support stomp protocol -->
<transportConnector name="stomp+nio" uri="stomp+nio://0.0.0.0:61613?transport.closeAsync=false"/>
...

5、使用 Stomp 庫對 ActiveMQ 進行收發消息測試(這裏不提供 Java 的例子,因爲 Google 上已經有太多了,我們以 PHP 作爲腳本語言的程序範例)。

a> 以下是 Stomp 協議的簡單命令:

* SEND
* SUBSCRIBE
* UNSUBSCRIBE
* BEGIN
* COMMIT
* ABORT
* ACK
* DISCONNECT

b> PHP 實例(transaction / persistent / ack)

關於 Stomp 的 PHP 庫可以到 Google Code 上下載,使用以下測試用例的時候可以配合 http://activemq-hostname:8161/admin/ 查看消息的數量和狀態。

c> amq_sent.php(生產者示例)
...
// include a library
require_once("Stomp.php");

// make a connection
$con = new Stomp("tcp://192.168.1.11:61613");

// connect
$con->connect("guest", "password");

// begin transaction
$con->begin("tx1");

try {
    
    // build a message by map
    require_once("Stomp/Message/Map.php");
    $msgBody = array("city"=>"Belgrade", "name"=>"Dejan");
    $msgHeader["persistent"] = "true"; // VERY IMPORTANT : Because By default, Stomp produced messages are set to non-persistent.
    $msgHeader["transformation"] = "jms-map-json";
    $mapMessage = new StompMessageMap($msgBody, $msgHeader);
    
    // send the message to the queue
    $con->send("/queue/test", $mapMessage, array("transaction" => "tx1"));
    
    // test rollback logic
//    throw new Exception("Sending failed ...");
    
    // commit sending message
    $con->commit("tx1");
    
    // print message
    echo "Sent message : ";
    print_r($msgBody);
    
} catch (Exception $e) {
    
    // rollback sending
    $con->abort("tx1");

    // print message
    echo $e->getMessage();
}

// disconnect
$con->disconnect();
...

d> amq_recv.php(消費者示例)
...
// include a library
require_once("Stomp.php");

// make a connection
$con = new Stomp("tcp://192.168.1.11:61613");

// connect with authentication
$con->connect("guest", "password");

// set read timeout
$con->setReadTimeout(1);

// subscribe to the queue
$con->subscribe("/queue/test", array("transformation" => "jms-map-json"));

// receive a message from the queue
$msg = $con->readFrame();

// do what you want with the message
if ( $msg != null) {
    echo "Received message : ";
    print_r($msg);
    // mark the message as received in the queue
    $con->ack($msg);
} else {
    echo "Failed to receive a message/n";
}

// disconnect
$con->disconnect();
...

6、目前 ActiveMQ 最新版的消息數據是保存在一個快速的文本數據庫 kahadb 裏面的,使用雖然速度很快但是一旦出錯恢復起來總是有很多的問題。所以我建議大家還是把主流數據庫的持久化選項打開,雖然慢一點但是數據至少恢復起來要簡單不少,我們使用 Mysql 來保存持久化的消息數據,數據庫是 activemq,配置在 ./conf/activemq.xml 中,如下:
...
<persistenceFactory>
    <journalPersistenceAdapterFactory dataDirectory="${activemq.base}/data" dataSource="#mysql-ds"/>
</persistenceFactory>
...
<!-- MySql DataSource Sample Setup -->
<bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://192.168.1.10:11811/activemq?relaxAutoCommit=true"/>
    <property name="username" value="admin"/>
    <property name="password" value="ihush2010"/>
    <property name="maxActive" value="200"/>
    <property name="poolPreparedStatements" value="true"/>
</bean>
...

總結&注意事項:
1、Stomp 協議構造的消息默認是非持久化的,如果要讓消息持久化必須加上 "persistent:true" 的消息頭,這個搞了我半天時間~
2、編程時候還是需要注意“消費者”的運行效率,因爲大量“慢消費者”會在非持久的 Topics 上出現問題,特別在多消費者的情況下,容易造成 ActiveMQ 的反應變慢~
3、如果不是使用 Stomp 協議和 ActiveMQ 建立通訊的情況下儘量使用長連接,Connection 的 start() 和 stop() 方法代價很高。
4、如果使用集羣 master-slave 也是可以的,但是總是不夠穩定,建議還是使用單臺服務器的模式。

發佈了95 篇原創文章 · 獲贊 18 · 訪問量 76萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章