ActiveMQ 初步入門及相關概念理解

 1 JMS
在介紹ActiveMQ之前,首先簡要介紹一下JMS規範。
11 JMS的基本構件
111 連接工廠
連接工廠是客戶用來創建連接的對象,例如ActiveMQ提供的ActiveMQConnectionFactory。

11.2 連接
JMS Connection封裝了客戶與JMS提供者之間的一個虛擬的連接。

11.3 會話
JMS Session是生產和消費消息的一個單線程上下文。會話用於創建消息生產者(producer)、消息消費者(consumer)和消息(message)等。會話提供了一個事務性的上下文,在這個上下文中,一組發送和接收被組合到了一個原子操作中。

11.4 目的地
目的地是客戶用來指定它生產的消息的目標和它消費的消息的來源的對象。JMS1.0.2規範中定義了兩種消息傳遞域:點對點(PTP)消息傳遞域和發佈/訂閱消息傳遞域。
點對點消息傳遞域的特點如下:
• 每個消息只能有一個消費者。
• 消息的生產者和消費者之間沒有時間上的相關性。無論消費者在生產者發送消息的時候是否處於運行狀態,它都可以提取消息。
發佈/訂閱消息傳遞域的特點如下:
• 每個消息可以有多個消費者。
• 生產者和消費者之間有時間上的相關性。訂閱一個主題的消費者只能消費自它訂閱之後發佈的消息。JMS規範允許客戶創建持久訂閱,這在一定程度上放鬆了時間上的相關性要求。持久訂閱允許消費者消費它在未處於激活狀態時發送的消息。
在點對點消息傳遞域中,目的地被成爲隊列(queue);在發佈/訂閱消息傳遞域中,目的地被成爲主題(topic)。

11.5 消息生產者
消息生產者是由會話創建的一個對象,用於把消息發送到一個目的地。

11.6 消息消費者
消息消費者是由會話創建的一個對象,它用於接收發送到目的地的消息。消息的消費可以採用以下兩種方法之一:
• 同步消費。通過調用消費者的receive方法從目的地中顯式提取消息。receive方法可以一直阻塞到消息到達。
• 異步消費。客戶可以爲消費者註冊一個消息監聽器,以定義在消息到達時所採取的動作。
11.7 消息
JMS消息由以下三部分組成:
• 消息頭。每個消息頭字段都有相應的getter和setter方法。
• 消息屬性。如果需要除消息頭字段以外的值,那麼可以使用消息屬性。
• 消息體。JMS定義的消息類型有TextMessage、MapMessage、BytesMessage、StreamMessage和ObjectMessage。
1.2 JMS的可靠性機制
1.2.1 確認
JMS消息只有在被確認之後,才認爲已經被成功地消費了。消息的成功消費通常包含三個階段:客戶接收消息、客戶處理消息和消息被確認。
在事務性會話中,當一個事務被提交的時候,確認自動發生。在非事務性會話中,消息何時被確認取決於創建會話時的應答模式(acknowledgement mode)。該參數有以下三個可選值:
• Session.AUTO_ACKNOWLEDGE。當客戶成功的從receive方法返回的時候,或者從MessageListener.onMessage方法成功返回的時候,會話自動確認客戶收到的消息。
• Session.CLIENT_ACKNOWLEDGE。 客戶通過消息的acknowledge方法確認消息。需要注意的是,在這種模式中,確認是在會話層上進行:確認一個被消費的消息將自動確認所有已被會話消 費的消息。例如,如果一個消息消費者消費了10個消息,然後確認第5個消息,那麼所有10個消息都被確認。
• Session.DUPS_ACKNOWLEDGE。 該選擇只是會話遲鈍第確認消息的提交。如果JMS provider失敗,那麼可能會導致一些重複的消息。如果是重複的消息,那麼JMS provider必須把消息頭的JMSRedelivered字段設置爲true。
1.2.2 持久性
JMS 支持以下兩種消息提交模式:
• PERSISTENT。指示JMS provider持久保存消息,以保證消息不會因爲JMS provider的失敗而丟失。
• NON_PERSISTENT。不要求JMS provider持久保存消息。
1.2.3 優先級
可以使用消息優先級來指示JMS provider首先提交緊急的消息。優先級分10個級別,從0(最低)到9(最高)。如果不指定優先級,默認級別是4。需要注意的是,JMS provider並不一定保證按照優先級的順序提交消息。

1.2.4 消息過期
可以設置消息在一定時間後過期,默認是永不過期。

1.2.5 臨時目的地
可以通過會話上的createTemporaryQueue方法和createTemporaryTopic方法來創建臨時目的地。它們的存在時間只限於創建它們的連接所保持的時間。只有創建該臨時目的地的連接上的消息消費者才能夠從臨時目的地中提取消息。

1.2.6 持久訂閱
首先消息生產者必須使用PERSISTENT提交消息。客戶可以通過會話上的createDurableSubscriber方法來創建一個持久訂閱,該方法的第一個參數必須是一個topic。第二個參數是訂閱的名稱。
JMS provider會存儲發佈到持久訂閱對應的topic上的消息。如果最初創建持久訂閱的客戶或者任何其它客戶使用相同的連接工廠和連接的客戶ID、相同 的主題和相同的訂閱名再次調用會話上的createDurableSubscriber方法,那麼該持久訂閱就會被激活。JMS provider會象客戶發送客戶處於非激活狀態時所發佈的消息。
持久訂閱在某個時刻只能有一個激活的訂閱者。持久訂閱在創建之後會一直保留,直到應用程序調用會話上的unsubscribe方法。

1.2.7 本地事務
在一個JMS客戶端,可以使用本地事務來組合消息的發送和接收。JMS Session接口提供了commit和rollback方法。事務提交意味着生產的所有消息被髮送,消費的所有消息被確認;事務回滾意味着生產的所有消 息被銷燬,消費的所有消息被恢復並重新提交,除非它們已經過期。
事務性的會話總是牽涉到事務處理中,commit或rollback方法一旦被調用,一個事務就結束了,而另一個事務被開始。關閉事務性會話將回滾其中的事務。
需要注意的是,如果使用請求/回覆機制,即發送一個消息,同時希望在同一個事務中等待接收該消息的回覆,那麼程序將被掛起,因爲知道事務提交,發送操作纔會真正執行。
需要注意的還有一個,消息的生產和消費不能包含在同一個事務中。

1.3 JMS 規範的變遷
JMS的最新版本的是1.1。它和同1.0.2版本之間最大的差別是,JMS1.1通過統一的消息傳遞域簡化了消息傳遞。這不僅簡化了JMS API,也有利於開發人員靈活選擇消息傳遞域,同時也有助於程序的重用和維護。
以下是不同消息傳遞域的相應接口:
JMS 公共 點對點域 發佈/訂閱域
ConnectionFactory QueueConnectionFactory TopicConnectionFactory
Connection QueueConnection TopicConnection
Destination Queue Topic
Session QueueSession TopicSession
MessageProducer QueueSender TopicPublisher
MessageConsumer QueueReceiver TopicSubscriber

2 ActiveMQ
2.1 Broker
2.11 Running Broker
ActiveMQ5.0 的二進制發佈包中bin目錄中包含一個名爲activemq的腳本,直接運行這個腳本就可以啓動一個broker。
此外也可以通過Broker Configuration URI或Broker XBean URI對broker進行配置,以下是一些命令行參數的例子:
Example Description
activemq Runs a broker using the default 'xbean:activemq.xml' as the broker configuration file.
activemq xbean:myconfig.xml Runs a broker using the file myconfig.xml as the broker configuration file that is located in the classpath.
activemq xbean:file:./conf/broker1.xml Runs a broker using the file broker1.xml as the broker configuration file that is located in the relative file path ./conf/broker1.xml
activemq xbean:file:C:/ActiveMQ/conf/broker2.xml Runs a broker using the file broker2.xml as the broker configuration file that is located in the absolute file path C:/ActiveMQ/conf/broker2.xml
activemq broker:(tcp://localhost:61616, tcp://localhost:5000)?useJmx=true Runs a broker with two transport connectors and JMX enabled.
activemq broker:(tcp://localhost:61616, network:tcp://localhost:5000)?persistent=false Runs a broker with 1 transport connector and 1 network connector with persistence disabled.

2.1.2 Embedded Broker
可以通過在應用程序中以編碼的方式啓動broker,例如:
Java代碼
1. BrokerService broker = new BrokerService();
2. broker.addConnector("tcp://localhost:61616");
3. broker.start();
如果需要啓動多個broker,那麼需要爲broker設置一個名字。例如:
Java代碼
1. BrokerService broker = new BrokerService();
2. broker.setName("fred");
3. broker.addConnector("tcp://localhost:61616");
4. broker.start();
如果希望在同一個JVM內訪問這個broker,那麼可以使用VM Transport,URI是:vm://brokerName。關於更多的broker屬性,可以參考Apache的官方文檔。
此外,也可以通過BrokerFactory來創建broker,例如:
Java代碼
1. BrokerService broker = BrokerFactory.createBroker(new URI(someURI));
someURI的可選值如下:
URI scheme Example Description
xbean: xbean:activemq.xml Searches the classpath for an XML document with the given URI (activemq.xml in this case) which will then be used as the Xml Configuration
file: file:foo/bar/activemq.xml Loads the given file (in this example foo/bar/activemq.xml) as the Xml Configuration
broker: broker:tcp://localhost:61616 Uses the Broker Configuration URI to configure the broker

當使用XBean的配置方式的時候,需要指定一個xml配置文件,例如:
Java代碼
1. BrokerService broker = BrokerFactory.createBroker(new URI("xbean:com/test/activemq.xml"));
使用Spring的配置方式如下:
Xml代碼
1. <bean id="broker" class="org.apache.activemq.xbean.BrokerFactoryBean">
2. <property name="config" value="classpath:org/apache/activemq/xbean/activemq.xml" />
3. <property name="start" value="true" />
4. </bean>

2.1.3 Monitoring Broker
2.1.3.1 JMX
在使用JMX監控broker之前,首先要啓用broker的JMX監控功能,例如在配置文件中設置useJmx="true",如下:
Xml代碼
1. <broker useJmx="true" brokerName="broker1>
2. <managementContext>
3. <managementContext createConnector="true"/>
4. </managementContext>
5. ...
6. </broker>
接下來運行JDK自帶的jconsole。在運行了jconsole後,它會彈出對話框來選擇需要連接到的agent。如果是在啓動broker的主機上 運行jconsole,那麼ActiveMQ broker會出現在jconsole的Local 標籤中。如果要連接到遠程的broker,那麼可以在Advanced標籤中指定JMX URL,以下是一個連接到本機的JMX URL:
service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi
在jconsole的MBeans標籤中,可以查看詳細信息,也可以執行相應的operation。需要注意的是,在jconsole連接到broker 的時候,並不需要輸入用戶名和密碼,如果這存在潛在的安全問題,那麼就需要爲JMX Connector配置密碼保護(需要使用1.5以上版本的JDK)。
首先要禁止ActiveMQ創建自己的connector,例如:
Xml代碼
1. <broker xmlns="http://activemq.org/config/1.0" brokerName="localhost"useJmx="true">
2. <managementContext>
3. <managementContext createConnector="false"/>
4. </managementContext>
5. </broker>
然後在ActiveMQ的conf目錄下創建一個訪問控制文件和密碼文件,如下:
conf/jmx.access:
# The "monitorRole" role has readonly access.
# The "controlRole" role has readwrite access.
monitorRole readonly
controlRole readwrite

conf/jmx.password:
# The "monitorRole" role has password "abc123".
# The "controlRole" role has password "abcd1234".
monitorRole abc123
controlRole abcd1234

然後修改ActiveMQ的bin目錄下activemq的啓動腳本,查找包含"SUNJMX="的一行如下:
REM set SUNJMX=-Dcom.sun.management.jmxremote.port=1616 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
把它替換成
set SUNJMX=-Dcom.sun.management.jmxremote.port=1616 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.password.file=%ACTIVEMQ_BASE%/conf/jmx.password -Dcom.sun.management.jmxremote.access.file=%ACTIVEMQ_BASE%/conf/jmx.access
最後重啓ActiveMQ和jconsole,這時候需要強制login。如果在啓動activemq的過程中出現以下錯誤,那麼需要爲這個文件增加訪問 控制。Windows平臺上的具體解決方法請參考如下網址:http://java.sun.com/j2se/1.5.0/docs/guide /management/security-windows.html
Error: Password file read access must be restricted: D:\apache-activemq-5.0.0\bin\../conf/jmx.password

2.1.3.2 Web Console
Web Console被集成到了ActiveMQ的二進制發佈包中,因此缺省訪問http://localhost:8161/admin即可訪問Web Console。
在配置文件中,可以通過修改nioConnector的port屬性來修改Web console的缺省端口:
Xml代碼
1. <jetty xmlns="http://mortbay.com/schemas/jetty/1.0">
2. <connectors>
3. <nioConnector port="8161" />
4. </connectors>
5. ...
6. </jetty>
出於安全性或者可靠性的考慮,Web Console 可以被部署到不同於ActiveMQ的進程中。例如把activemq-web-console.war部署到一個單獨的web容器中 (Tomcat,Jetty等)。在ActiveMQ5.0的二進制發佈包中不包含activemq-web-console.war,因此需要下載 ActiveMQ的源碼,然後進入到${activemq.base}/src/activemq-web-console目錄中執行mvn instanll。如果一切正常,那麼缺省會在${activemq.base}/src/activemq-web-console/target目錄 中生成activemq-web-console-5.0.0.war。然後將activemq-web-console-5.0.0.war拷貝到 Tomcat的webapps目錄中,並重命名成activemq-web-console.war。
需要注意的是,要將activemq-all-5.0.0.jar拷貝到WEB-INF\lib目錄中(可能還需要拷貝jms.jar)。還要爲Tomcat設置以下五個系統屬性(修改catalina.bat文件):
set JAVA_OPTS=%JAVA_OPTS% -Dwebconsole.type="properties"
set JAVA_OPTS=%JAVA_OPTS% -Dwebconsole.jms.url="tcp://localhost:61616"
set JAVA_OPTS=%JAVA_OPTS% -Dwebconsole.jmx.url="service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"
set JAVA_OPTS=%JAVA_OPTS% -Dwebconsole.jmx.role=""
set JAVA_OPTS=%JAVA_OPTS% -Dwebconsole.jmx.password=""
如果JMX沒有配置密碼保護,那麼webconsole.jmx.role和webconsole.jmx.password設置成""即可。如果 broker被配置成了Master/Slave模式,那麼可以配置成使用failover transport,例如:
-Dwebconsole.jms.url=failover:(tcp://serverA:61616,tcp://serverB:61616)
順便說一下,由於webconsole.type 屬性是properties,因此實際上起作用的Web Console的配置文件是WEB-INF/ webconsole-properties.xml。最後啓動被監控的ActiveMQ,訪問http://localhost:8080 /activemq-web-console/,查看顯示是否正常。

2.1.3.3 Advisory Message
ActiveMQ 支持Advisory Messages,它允許你通過標準的JMS 消息來監控系統。目前的Advisory Messages支持:
• consumers, producers and connections starting and stopping
• temporary destinations being created and destroyed
• messages expiring on topics and queues
• brokers sending messages to destinations with no consumers.
• connections starting and stopping
Advisory Messages可以被想象成某種的管理通道,通過它你可以得到關於JMS provider、producers、consumers和destinations的信息。Advisory topics都使用ActiveMQ.Advisory.這個前綴,以下是目前支持的topics:
Client based advisories
Advisory Topics Description
ActiveMQ.Advisory.Connection Connection start & stop messages
ActiveMQ.Advisory.Producer.Queue Producer start & stop messages on a Queue
ActiveMQ.Advisory.Producer.Topic Producer start & stop messages on a Topic
ActiveMQ.Advisory.Consumer.Queue Consumer start & stop messages on a Queue
ActiveMQ.Advisory.Consumer.Topic Consumer start & stop messages on a Topic

在消費者啓動/停止的Advisory Messages的消息頭中有個consumerCount屬性,他用來指明目前desination上活躍的consumer的數量。
Destination and Message based advisories
Advisory Topics Description
ActiveMQ.Advisory.Queue Queue create & destroy
ActiveMQ.Advisory.Topic Topic create & destroy
ActiveMQ.Advisory.TempQueue Temporary Queue create & destroy
ActiveMQ.Advisory.TempTopic Temporary Topic create & destroy
ActiveMQ.Advisory.Expired.Queue Expired messages on a Queue
ActiveMQ.Advisory.Expired.Topic Expired messages on a Topic
ActiveMQ.Advisory.NoConsumer.Queue No consumer is available to process messages being sent on a Queue
ActiveMQ.Advisory.NoConsumer.Topic No consumer is available to process messages being sent on a Topic
以上的這些destnations都可以用來作爲前綴,在其後面追加其它的重要信息,例如topic、queue、clientID、 producderID和consumerID等。這令你可以利用Wildcards 和 Selectors 來過濾Advisory Messages(關於Wildcard和Selector會在稍後介紹)。
例如,如果你希望訂閱FOO.BAR這個queue上Consumer的start/stop的消息,那麼可以訂閱 ActiveMQ.Advisory.Consumer.Queue.FOO.BAR;如果希望訂閱所有queue上的start/stop消息,那麼可 以訂閱ActiveMQ.Advisory.Consumer.Queue.>;如果希望訂閱所有queue或者topic上的 start/stop消息,那麼可以訂閱ActiveMQ.Advisory.Consumer. >。
org.apache.activemq.advisory.AdvisorySupport類上有如下的helper methods,用來在程序中得到advisory destination objects。
Java代碼
1. AdvisorySupport.getConsumerAdvisoryTopic()
2. AdvisorySupport.getProducerAdvisoryTopic()
3. AdvisorySupport.getDestinationAdvisoryTopic()
4. AdvisorySupport.getExpiredTopicMessageAdvisoryTopic()
5. AdvisorySupport.getExpiredQueueMessageAdvisoryTopic()
6. AdvisorySupport.getNoTopicConsumersAdvisoryTopic()
7. AdvisorySupport.getNoQueueConsumersAdvisoryTopic()
以下是段使用Advisory Messages的程序代碼:
Java代碼
1. Destination advisoryDestination = AdvisorySupport.getProducerAdvisoryTopic(destination)
2. MessageConsumer consumer = session.createConsumer(advisoryDestination);
3. consumer.setMessageListener(this);
4. ...
5. public void onMessage(Message msg){
6. if (msg instanceof ActiveMQMessage){
7. try {
8. ActiveMQMessage aMsg = (ActiveMQMessage)msg;
9. ProducerInfo prod = (ProducerInfo) aMsg.getDataStructure();
10. } catch (JMSException e) {
11. log.error("Failed to process message: " + msg);
12. }
13. }
14. }

2.1.3.4 Command Agent
在介紹Command Agent前首先簡要介紹一下XMPP(Jabber)協議,XMPP是一種基於XML的即時通信協議,它由Jabber軟件基金會開發。在配置文件中通過增加transportConnector來支持XMPP協議:
Xml代碼
1. <broker xmlns="http://activemq.org/config/1.0">
2. <transportConnectors>
3. ...
4. <transportConnector name="xmpp" uri="xmpp://localhost:61222"/>
5. </transportConnectors>
6. </broker>
ActiveMQ提供了ActiveMQ messages和XMPP之間的雙向橋接:
• 如果客戶加入了一個聊天室,那麼這個聊天室的名字會被映射到一個JMS topic。
• 嘗試在聊天室內發送消息會導致一個JMS消息被髮送到這個topic。
• 呆在一個聊天室中意味着這將保持一個對相應JMS topic的訂閱。因此發送到這個topic的JMS消息也會被髮送到聊天室。
推薦XMPP客戶端Spark(http://www.igniterealtime.org/)。
從4.2版本起,ActiveMQ支持Command Agent。在配置文件中,通過設置commandAgent來啓用Command Agent:
Xml代碼
1. <beans>
2. <broker useJmx="true" xmlns="http://activemq.org/config/1.0">
3. ...
4. </broker>
5. <commandAgent xmlns="http://activemq.org/config/1.0"/>
6. </beans>
啓用了Command Agent的broker上會有一個來自Command Agent的連接,它同時訂閱topic: ActiveMQ.Agent。在你啓動XMPP客戶端,加入到ActiveMQ.Agent聊天室後,就可以同broker進行交談了。通過在XMPP 客戶端中鍵入help,可以得到幫助信息。
需要注意的是,ActiveMQ5.0版本有個小bug,如果broker沒有采用缺省的用戶名和密碼,那麼Command Agent便無法正常啓動。Apache官方文檔說,此bug已經被修正,預定在5.2.0版本上體現。修改方式如下:
Xml代碼
1. <commandAgent xmlns="http://activemq.org/config/1.0" brokerUser="user" brokerPassword="passward"/>

2.1.3.5 Visualization plugin
ActiveMQ支持以broker插件的形式生成DOT文件(可以用agrviewer來查看),以圖表的方式描述connections、sessions、producers、consumers、destinations等信息。配置方式如下:
Xml代碼
1. <broker xmlns="http://activemq.org/config/1.0" brokerName="localhost" useJmx="true">
2. ...
3. <plugins>
4. <connectionDotFilePlugin file="connection.dot"/>
5. <destinationDotFilePlugin file="destination.dot"/>
6. </plugins>
7. </broker>
需要注意的是,筆者認爲ActiveMQ5.0版本的Visualization Plugin尚不穩定,存在諸多問題。例如:如果使用connectionDotFilePlugin,那麼brokerName必須是 localhost;如果使用destinationDotFilePlugin可能會導致ArrayStoreException。
2.2 Transport
ActiveMQ目前支持的transport有:VM Transport、TCP Transport、SSL Transport、Peer Transport、UDP Transport、Multicast Transport、HTTP and HTTPS Transport、Failover Transport、Fanout Transport、Discovery Transport、ZeroConf Transport等。以下簡單介紹其中的幾種,更多請參考Apache官方文檔。

2.2.1 VM Transport
VM transport允許在VM內部通信,從而避免了網絡傳輸的開銷。這時候採用的連接不是socket連接,而是直接地方法調用。 第一個創建VM 連接的客戶會啓動一個embed VM broker,接下來所有使用相同的broker name的VM連接都會使用這個broker。當這個broker上所有的連接都關閉的時候,這個broker也會自動關閉。
以下是配置語法:
vm://brokerName?transportOptions
例如:vm://broker1?marshal=false&broker.persistent=false
Transport Options的可選值如下:
Option Name Default Value Description
Marshal false If true, forces each command sent over the transport to be marshlled and unmarshlled using a WireFormat
wireFormat default The name of the WireFormat to use
wireFormat.* All the properties with this prefix are used to configure the wireFormat
create true If the broker should be created on demand if it does not allready exist. Only supported in ActiveMQ 4.1
broker.* All the properties with this prefix are used to configure the broker. See Configuring Wire Formats for more information

以下是高級配置語法:
vm:(broker:(tcp://localhost)?brokerOptions)?transportOptions
vm:broker:(tcp://localhost)?brokerOptions
例如:vm:(broker:(tcp://localhost:6000)?persistent=false)?marshal=false
Transport Options的可選值如下:
Option Name Default Value Description
marshal false If true, forces each command sent over the transport to be marshlled and unmarshlled using a WireFormat
wireFormat default The name of the WireFormat to use
wireFormat.* All the properties with this prefix are used to configure the wireFormat

使用配置文件的配置語法:
vm://localhost?brokerConfig=xbean:activemq.xml
例如:vm:// localhost?brokerConfig=xbean:com/test/activemq.xml

使用Spring的配置:
Xml代碼
1. <bean id="broker" class="org.apache.activemq.xbean.BrokerFactoryBean">
2. <property name="config" value="classpath:org/apache/activemq/xbean/activemq.xml" />
3. <property name="start" value="true" />
4. </bean>
5.
6. <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" depends-on="broker">
7. <property name="brokerURL" value="vm://localhost"/>
8. </bean>
如果persistent是true,那麼ActiveMQ會在當前目錄下創建一個缺省值是activemq-data的目錄用於持久化保存數據。需要注 意的是,如果程序中啓動了多個不同名字的VM broker,那麼可能會有如下警告:Failed to start jmx connector: Cannot bind to URL [rmi://localhost:1099/jmxrmi]: javax.naming.NameAlreadyBoundException…可以通過在transportOptions中追加 broker.useJmx=false來禁用JMX來避免這個警告。

2.2.2 TCP Transport
TCP transport 允許客戶端通過TCP socket連接到遠程的broker。以下是配置語法:
tcp://hostname:port?transportOptions
Transport Options的可選值如下:
Option Name Default Value Description
minmumWireFormatVersion 0 The minimum version wireformat that is allowed
trace false Causes all commands that are sent over the transport to be logged
useLocalHost true When true, it causes the local machines name to resolve to "localhost".
socketBufferSize 64 * 1024 Sets the socket buffer size in bytes
soTimeout 0 sets the socket timeout in milliseconds
connectionTimeout 30000 A non-zero value specifies the connection timeout in milliseconds. A zero value means wait forever for the connection to be established. Negative values are ignored.
wireFormat default The name of the WireFormat to use
wireFormat.* All the properties with this prefix are used to configure the wireFormat. See Configuring Wire Formats for more information
例如:tcp://localhost:61616?trace=false

2.2.3 Failover Transport
Failover Transport是一種重新連接的機制,它工作於其它transport的上層,用於建立可靠的傳輸。它的配置語法允許制定任意多個複合的URI。 Failover transport會自動選擇其中的一個URI來嘗試建立連接。如果沒有成功,那麼會選擇一個其它的URI來建立一個新的連接。以下是配置語法:
failover:(uri1,...,uriN)?transportOptions
failover:uri1,...,uriN
Transport Options的可選值如下:
Option Name Default Value Description
initialReconnectDelay 10 How long to wait before the first reconnect attempt (in ms)
maxReconnectDelay 30000 The maximum amount of time we ever wait between reconnect attempts (in ms)
useExponentialBackOff true Should an exponential backoff be used between reconnect attempts
backOffMultiplier 2 The exponent used in the exponential backoff attempts
maxReconnectAttempts 0 If not 0, then this is the maximum number of reconnect attempts before an error is sent back to the client
randomize true use a random algorithm to choose the URI to use for reconnect from the list provided
backup false initialize and hold a second transport connection - to enable fast failover
例如:failover:(tcp://localhost:61616,tcp://remotehost:61616)?initialReconnectDelay=100

2.2.4 Discovery transport
Discovery transport是可靠的tranport。它使用Discovery transport來定位用來連接的URI列表。以下是配置語法:
discovery:(discoveryAgentURI)?transportOptions
discovery:discoveryAgentURI
Transport Options的可選值如下:
Option Name Default Value Description
initialReconnectDelay 10 How long to wait before the first reconnect attempt
maxReconnectDelay 30000 The maximum amount of time we ever wait between reconnect attempts
useExponentialBackOff true Should an exponential backoff be used btween reconnect attempts
backOffMultiplier 2 The exponent used in the exponential backoff attempts
maxReconnectAttempts 0 If not 0, then this is the maximum number of reconnect attempts before an error is sent back to the client
例如:discovery:(multicast://default)?initialReconnectDelay=100
爲了使用Discovery來發現broker,需要爲broker啓用discovery agent。 以下是XML配置文件中的一個例子:
Xml代碼
1. <broker name="foo">
2. <transportConnectors>
3. <transportConnector uri="tcp://localhost:0" discoveryUri="multicast://default"/>
4. </transportConnectors>
5. ...
6. </broker>
在使用Failover Transport或Discovery transport等能夠自動重連的transport的時候,需要注意的是:設想有兩個broker,它們都啓用AMQ Message Store作爲持久化存儲,有一個producer和一個consumer連接到某個queue。當因其中一個broker失效時而切換到另一個 broker的時候,如果失效的broker的queue中還有未被consumer消費的消息,那麼這個queue裏的消息仍然滯留在失效broker 的中,直到失效的broker被修復並重新切換回這個被修復的broker後,之前被保留的消息纔會被consumer消費掉。如果被處理的消息有時序限 制,那麼應用程序就需要處理這個問題。另外也可以通過ActiveMQ集羣來解決這個問題。
在transport重連的時候,可以在connection上註冊TransportListener來獲得回調,例如:
Java代碼
1. (ActiveMQConnection)connection).addTransportListener(new TransportListener() {
2. public void onCommand(Object cmd) {
3. }
4.
5. public void onException(IOException exp) {
6. }
7.
8. public void transportInterupted() {
9. // The transport has suffered an interruption from which it hopes to recover.
10. }
11.
12. public void transportResumed() {
13. // The transport has resumed after an interruption.
14. }
15. });
2.3 Persistence
2.3.1 AMQ Message Store
AMQ Message Store是ActiveMQ5.0缺 省的持久化存儲。Message commands被保存到transactional journal(由rolling data logs組成)。Messages被保存到data logs中,同時被reference store進行索引以提高存取速度。Date logs由一些單獨的data log文件組成,缺省的文件大小是32M,如果某個消息的大小超過了data log文件的大小,那麼可以修改配置以增加data log文件的大小。如果某個data log文件中所有的消息都被成功消費了,那麼這個data log文件將會被標記,以便在下一輪的清理中被刪除或者歸檔。以下是其配置的一個例子:
Xml代碼
1. <broker brokerName="broker" persistent="true" useShutdownHook="false">
2. <persistenceAdapter>
3. <amqPersistenceAdapter directory="${activemq.base}/data" maxFileLength="32mb"/>
4. </persistenceAdapter>
5. </broker>
Property name Default value Comments
directory activemq-data the path to the directory to use to store the message store data and log files
useNIO true use NIO to write messages to the data logs
syncOnWrite false sync every write to disk
maxFileLength 32mb a hint to set the maximum size of the message data logs
persistentIndex true use a persistent index for the message logs. If this is false, an in-memory structure is maintained
maxCheckpointMessageAddSize 4kb the maximum number of messages to keep in a transaction before automatically committing
cleanupInterval 30000 time (ms) before checking for a discarding/moving message data logs that are no longer used
indexBinSize 1024 default number of bins used by the index. The bigger the bin size - the better the relative performance of the index
indexKeySize 96 the size of the index key - the key is the message id
indexPageSize 16kb the size of the index page - the bigger the page - the better the write performance of the index
directoryArchive archive the path to the directory to use to store discarded data logs
archiveDataLogs false if true data logs are moved to the archive directory instead of being deleted

2.3.2 Kaha Persistence
Kaha Persistence 是一個專門針對消息持久化的解決方案。它對典型的消息使用模式進行了優化。在Kaha中,數據被追加到data logs中。當不再需要log文件中的數據的時候,log文件會被丟棄。以下是其配置的一個例子:
Xml代碼
1. <broker brokerName="broker" persistent="true" useShutdownHook="false">
2. <persistenceAdapter>
3. <kahaPersistenceAdapter directory="activemq-data" maxDataFileLength="33554432"/>
4. </persistenceAdapter>
5. </broker>

2.3.3 JDBC Persistence
目前支持的數據庫有Apache Derby, Axion, DB2, HSQL, Informix, MaxDB, MySQL, Oracle, Postgresql, SQLServer, Sybase。
如果你使用的數據庫不被支持,那麼可以調整StatementProvider 來保證使用正確的SQL方言(flavour of SQL)。通常絕大多數數據庫支持以下adaptor:
• org.activemq.store.jdbc.adapter.BlobJDBCAdapter
• org.activemq.store.jdbc.adapter.BytesJDBCAdapter
• org.activemq.store.jdbc.adapter.DefaultJDBCAdapter
• org.activemq.store.jdbc.adapter.ImageJDBCAdapter
也可以在配置文件中直接指定JDBC adaptor,例如:
Xml代碼
1. <jdbcPersistenceAdapter adapterClass="org.apache.activemq.store.jdbc.adapter.ImageBasedJDBCAdaptor"/>
以下是其配置的一個例子:

Xml代碼
1. <persistence>
2. <jdbcPersistence dataSourceRef=" mysql-ds"/>
3. </persistence>
4.
5. <bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
6. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
7. <property name="url" value="jdbc:mysql://localhost/activemq?relaxAutoCommit=true"/>
8. <property name="username" value="activemq"/>
9. <property name="password" value="activemq"/>
10. <property name="poolPreparedStatements" value="true"/>
11. </bean>
需要注意的是,如果使用MySQL,那麼需要設置relaxAutoCommit 標誌爲true。

2.3.4 Disable Persistence
以下是其配置的一個例子:
Xml代碼
1. <broker persistent="false">
2. </broker>
2.4 Security
ActiveMQ支持可插拔的安全機制,用以在不同的provider之間切換。
2.4.1 Simple Authentication Plugin
Simple Authentication Plugin適用於簡單的認證需求,或者用於建立測試環境。它允許在XML配置文件中指定用戶、用戶組和密碼等信息。以下是ActiveMQ配置的一個例子:
Xml代碼
1. <plugins>
2. ...
3. <simpleAuthenticationPlugin>
4. <users>
5. <authenticationUser username="system" password="manager" groups="users,admins"/>
6. <authenticationUser username="user" password="password" groups="users"/>
7. <authenticationUser username="guest" password="password" groups="guests"/>
8. </users>
9. </simpleAuthenticationPlugin>
10. </plugins>

2.4.2 JAAS Authentication Plugin
JAAS Authentication Plugin依賴標準的JAAS機制來實現認證。通常情況下,你需要通過設置java.security.auth.login.config系統屬性來 配置login modules的配置文件。如果沒有指定這個系統屬性,那麼JAAS Authentication Plugin會缺省使用login.config作爲文件名。以下是一個login.config文件的例子:
activemq-domain {
org.apache.activemq.jaas.PropertiesLoginModule required debug=true org.apache.activemq.jaas.properties.user="users.properties" org.apache.activemq.jaas.properties.group="groups.properties";
};
這個login.config文件中設置了兩個屬性:org.apache.activemq.jaas.properties.user和 org.apache.activemq.jaas.properties.group分別用來指向user.properties和 group.properties文件。需要注意的是,PropertiesLoginModule使用本地文件的查找方式,而且查找時採用的base directory是login.config文件所在的目錄。因此這個login.config說明user.properties和 group.properties文件存放在跟login.config文件相同的目錄裏。
以下是ActiveMQ配置的一個例子:
Xml代碼
1. <plugins>
2. ...
3. <jaasAuthenticationPlugin configuration="activemq-domain" />
4. </plugins>
基於以上的配置,在JAAS的LoginContext中會使用activemq-domain中配置的PropertiesLoginModule來進行登陸。
ActiveMQ JAAS還支持LDAPLoginModule、CertificateLoginModule、TextFileCertificateLoginModule等login module。

2.4.3 Custom Authentication Implementation
可以通過編碼的方式爲ActiveMQ增加認證功能。例如編寫一個類繼承自XBeanBrokerService。
Java代碼
1. package com.yourpackage;
2.
3. import java.net.URI;
4. import java.util.HashMap;
5. import java.util.Map;
6.
7. import org.apache.activemq.broker.Broker;
8. import org.apache.activemq.broker.BrokerFactory;
9. import org.apache.activemq.broker.BrokerService;
10. import org.apache.activemq.security.SimpleAuthenticationBroker;
11. import org.apache.activemq.xbean.XBeanBrokerService;
12.
13. public class SimpleAuthBroker extends XBeanBrokerService {
14. //
15. private String user;
16. private String password;
17.
18. @SuppressWarnings("unchecked")
19. protected Broker addInterceptors(Broker broker) throws Exception {
20. broker = super.addInterceptors(broker);
21. Map passwords = new HashMap();
22. passwords.put(getUser(), getPassword());
23. broker = new SimpleAuthenticationBroker(broker, passwords, new HashMap());
24. return broker;
25. }
26.
27. public String getUser() {
28. return user;
29. }
30.
31. public void setUser(String user) {
32. this.user = user;
33. }
34.
35. public String getPassword() {
36. return password;
37. }
38.
39. public void setPassword(String password) {
40. this.password = password;
41. }
42. }
以下是ActiveMQ配置文件的一個例子:
Xml代碼
1. <beans>
2. …
3. <auth:SimpleAuthBroker
4. xmlns:auth="java://com.yourpackage"
5. xmlns="http://activemq.org/config/1.0" brokerName="SimpleAuthBroker1" user="user" password="password" useJmx="true">
6.
7. <transportConnectors>
8. <transportConnector uri="tcp://localhost:61616"/>
9. </transportConnectors>
10. </auth:SimpleAuthBroker>
11. …
12. </beans>
在這個配置文件中增加了一個namespace auth,用於指向之前編寫的哪個類。同時爲SimpleAuthBroker注入了兩個屬性值user和password,因此在被 SimpleAuthBroker改寫的addInterceptors方法裏,可以使用這兩個屬性進行認證了。ActiveMQ提供的 SimpleAuthenticationBroker類繼承自BrokerFilter(可以簡單的看成是Broker的Adaptor),它的構造函 數中的兩個Map分別是userPasswords和userGroups。 SimpleAuthenticationBroker在 addConnection方法中使用userPasswords進行認證,同時會把userGroups的信息保存到 ConnectionContext中 。

2.4.4 Authorization Plugin
可以通過Authorization Plugin爲認證後的用戶授權,以下ActiveMQ配置文件的一個例子:
Xml代碼
1. <plugins>
2. <jaasAuthenticationPlugin configuration="activemq-domain"/>
3.
4. <authorizationPlugin>
5. <map>
6. <authorizationMap>
7. <authorizationEntries>
8. <authorizationEntry queue=">" read="admins" write="admins" admin="admins" />
9. <authorizationEntry queue="USERS.>" read="users" write="users" admin="users" />
10. <authorizationEntry queue="GUEST.>" read="guests" write="guests,users" admin="guests,users" />
11.
12. <authorizationEntry topic=">" read="admins" write="admins" admin="admins" />
13. <authorizationEntry topic="USERS.>" read="users" write="users" admin="users" />
14. <authorizationEntry topic="GUEST.>" read="guests" write="guests,users" admin="guests,users" />
15.
16. <authorizationEntry topic="ActiveMQ.Advisory.>" read="guests,users" write="guests,users" admin="guests,users"/>
17. </authorizationEntries>
18. </authorizationMap>
19. </map>
20. </authorizationPlugin>
21. </plugins>
2.5 Clustering
ActiveMQ從多種不同的方面提供了集羣的支持。
2.5.1 Queue consumer clusters
ActiveMQ支持訂閱同一個queue的consumers上的集羣。如果一個consumer失效,那麼所有未被確認 (unacknowledged)的消息都會被髮送到這個queue上其它的consumers。如果某個consumer的處理速度比其它 consumers更快,那麼這個consumer就會消費更多的消息。
需要注意的是,筆者發現AcitveMQ5.0版本的Queue consumer clusters存在一個bug:採用AMQ Message Store,運行一個producer,兩個consumer,並採用如下的配置文件:
Xml代碼
1. <beans>
2. <broker xmlns="http://activemq.org/config/1.0" brokerName="BugBroker1" useJmx="true">
3.
4. <transportConnectors>
5. <transportConnector uri="tcp://localhost:61616"/>
6. </transportConnectors>
7.
8. <persistenceAdapter>
9. <amqPersistenceAdapter directory="activemq-data/BugBroker1" maxFileLength="32mb"/>
10. </persistenceAdapter>
11.
12. </broker>
13. </beans>
那麼經過一段時間後可能會報出如下錯誤:
ERROR [ActiveMQ Transport: tcp:///127.0.0.1:1843 - RecoveryListenerAdapter.java:58 - RecoveryListenerAdapter] Message id ID:versus-1837-1203915536609-0:2:1:1:419 could not be recovered from the data store!
Apache官方文檔說,此bug已經被修正,預定在5.1.0版本上體現。

2.5.2 Broker clusters
一個常見的場景是有多個JMS broker,有一個客戶連接到其中一個broker。如果這個broker失效,那麼客戶會自動重新連接到其它的broker。在ActiveMQ中使 用failover:// 協議來實現這個功能。ActiveMQ3.x版本的reliable://協議已經變更爲failover://。
如果某個網絡上有多個brokers而且客戶使用靜態發現(使用Static Transport或Failover Transport)或動態發現(使用Discovery Transport),那麼客戶可以容易地在某個broker失效的情況下切換到其它的brokers。然而,stand alone brokers並不瞭解其它brokers上的consumers,也就是說如果某個broker上沒有consumers,那麼這個broker上的消 息可能會因得不到處理而積壓起來。目前的解決方案是使用Network of brokers,以便在broker之間存儲轉發消息。ActiveMQ在未來會有更好的特性,用來在客戶端處理這個問題。
從ActiveMQ1.1版本起,ActiveMQ支持networks of brokers。它支持分佈式的queues和topics。一個broker會相同對待所有的訂閱(subscription):不管他們是來自本地的 客戶連接,還是來自遠程broker,它都會遞送有關的消息拷貝到每個訂閱。遠程broker得到這個消息拷貝後,會依次把它遞送到其內部的本地連接上。 有兩種方式配置Network of brokers,一種是使用static transport,如下:
Xml代碼
1. <broker brokerName="receiver" persistent="false" useJmx="false">
2. <transportConnectors>
3. <transportConnector uri="tcp://localhost:62002"/>
4. </transportConnectors>
5. <networkConnectors>
6. <networkConnector uri="static:( tcp://localhost:61616,tcp://remotehost:61616)"/>
7. </networkConnectors>
8. …
9. </broker>
另外一種是使用multicast discovery,如下:
Xml代碼
1. <broker name="sender" persistent="false" useJmx="false">
2. <transportConnectors>
3. <transportConnector uri="tcp://localhost:0" discoveryUri="multicast://default"/>
4. </transportConnectors>
5. <networkConnectors>
6. <networkConnector uri="multicast://default"/>
7. </networkConnectors>
8. ...
9. </broker>
Network Connector有以下屬性:
Property Default Value Description
name bridge name of the network - for more than one network connector between the same two brokers - use different names
dynamicOnly false if true, only forward messages if a consumer is active on the connected broker
decreaseNetworkConsumerPriority false decrease the priority for dispatching to a Queue consumer the further away it is (in network hops) from the producer
networkTTL 1 the number of brokers in the network that messages and subscriptions can pass through
conduitSubscriptions true multiple consumers subscribing to the same destination are treated as one consumer by the network
excludedDestinations empty destinations matching this list won't be forwarded across the network
dynamicallyIncludedDestinations empty destinations that match this list will be forwarded across the network n.b. an empty list means all destinations not in the excluded list will be forwarded
staticallyIncludedDestinations empty destinations that match will always be passed across the network - even if no consumers have ever registered an interest
duplex false if true, a network connection will be used to both produce AND Consume messages. This is useful for hub and spoke scenarios when the hub is behind a firewall etc.
關於conduitSubscriptions屬性,這裏稍稍說明一下。設想有兩個brokers,分別是brokerA和brokerB,它們之間用 forwarding bridge連接。有一個consumer連接到brokerA並訂閱queue:Q.TEST。有兩個consumers連接到brokerB,也是訂 閱queue:Q.TEST。這三個consumers有相同的優先級。然後啓動一個producer。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章