原帖::http://blog.163.com/zqy216_2008/blog/static/4119371820107253335260/
一、JMS的理解
JMS概述:
jms即JavaMessage Service(Java Message Service)應用程式介面是一個Java平台中關於面向訊息中介軟體(MOM)的API,用於在兩個應用程式之間,或分布式系統中發送訊息,進行非同步通訊。JavaMessage Service是一個與具體平台無關的API,絕大多數MOM供應商都對JMS提供支援。
JMS元素:
JMS有以下元素組成。
JMS提供者
串連面向訊息中介軟體的,JMS介面的一個實現。提供者可以是Java平台的JMS實現,也可以是非Java平台的面向訊息中介軟體的適配器
JMS客戶
生產或消費訊息的基於Java的應用程式或對象。
JMS生產者
建立並發送訊息的JMS客戶。
JMS消費者
接收訊息的JMS客戶。
JMS訊息
包括可以在JMS客戶之間傳遞的資料的對象
JMS隊列
一個容納那些被發送的等待閱讀的訊息的地區。隊列暗示,這些訊息將按照順序發送。一旦一個訊息被閱讀,該訊息將被從隊列中移走。
JMS主題
一種支援發送訊息給多個訂閱者的機制。
JMS模型:
JavaMessage Service應用程式結構支援兩種模型:
點對點或隊列模型
發行者/訂閱者模型
在點對點或隊列模型下,一個生產者向一個特定的隊列發布訊息,一個消費者從該隊列中讀取訊息。這裡,生產者知道消費者的隊列,並直接將訊息發送到消費者的隊列。這種模式被概括為:
只有一個消費者將獲得訊息
生產者不需要在接收者消費該訊息期間處於運行狀態,接收者也同樣不需要在訊息發送時處於運行狀態。
每一個成功處理的訊息都由接收者簽收
發行者/訂閱者模型支援向一個特定的訊息主題發布訊息。0或多個訂閱者可能對接收來自特定訊息主題的訊息感興趣。在這種模型下,發行者和訂閱者彼此不知道對方。這種模式好比是匿名公告板。這種模式被概括為:
多個消費者可以獲得訊息
在發行者和訂閱者之間存在時間依賴性。發行者需要建立一個訂閱(subscription),以便客戶能夠購訂閱。訂閱者必須保持持續的活動狀態以接收訊息,除非訂閱者建立了持久的訂閱。在那種情況下,在訂閱者未串連時發布的訊息將在訂閱者重新串連時重新發布。
使用Java語言,JMS提供了將應用與提供資料的傳輸層相分離的方式。同一組Java類可以通過JNDI中關於提供者的資訊,串連不同的JMS提供者。這一組類首先使用一個串連工廠以串連到隊列或主題,然後發送或發布訊息。在接收端,客戶接收或訂閱這些訊息。
二、 JMS的實現提供者(ActiveMQ):
1.windows版:
A.在http://activemq.apache.org/ 官網上下載apache-activemq-5.3.0-bin.zip
B.下載後解壓縮,執行bin檔案夾中的activemq.bat 將啟動預設配置的activemq
C.通過http://localhost:8161/admin訪問activemq的管理介面。查看隊列(queue)和主題(topic)下的訊息內容。
2.linux版:
A.在http://activemq.apache.org/ 官網上下載apache-activemq-5.3.0-bin.tar.gz
B.解壓
將下載下來的apache-activemq-5.3.0-bin.tar.gz放置於/usr/local
cd /usr/local/
tar xvfz apache-activemq-5.3.0-bin.tar.gz
則ActiveMQ的安裝目錄為:/usr/local/apache-activemq-5.3.0
C.修改activemq運行檔案的許可權
cd /usr/local/apache-activemq-5.2.0/bin
chmod 755 activemq
D.運行
(1).普通啟動
bin/activemq
(2).指定記錄檔的啟動方式
bin/activemq >/tmp/smlog 2>&1 &
(3)後台啟動方式
nohup bin/activemq >/tmp/smlog 2>&1 &
這裡需要注意:前兩種啟動方式在linix命令列或者通過ssh用戶端啟動時在關閉對應的視窗時activemq會關閉,甚至直接ctrl+c也會導致activemq退出;第三種則不會出現這種情況。
E.驗證服務端是否開啟,有幾種方法
(1).查看控制台輸出或者記錄檔
(2).直接存取activemq的管理頁面:http://localhost:8161/admin/
F.關閉activemq
如果開啟方式是使用(1)或(2),則直接ctrl+c或者關閉對應的終端即可
如果開啟方式是(3),則稍微麻煩一點:
先尋找到activemq對應的進程:
ps -ef | grep activemq
然後把對應的進程殺掉,假設找到的進程編號為 168168
kill 168168
3.ActiveMQ的activemq.xml詳細配置講解
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.org/config/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.org/config/1.0 http://activemq.apache.org/schema/activemq-core.xsd
http://activemq.apache.org/camel/schema/spring>
<!-- persistent="true"表示要持久化儲存訊息,和子項目persistenceAdapter結合使用 -->
<!-- dataDirectory預設的儲存持久化資料的目錄 -->
<!-- brokerName 設定broker的name,在注意在網路上必須是唯一的-->
<broker xmlns="http://activemq.org/config/1.0" brokerName="192.168.1.148" persistent ="true" dataDirectory="${activemq.base}/data" useShutdownHook="false">
<!-- Destination specific policies using destination names or wildcards -->
<!-- wildcards意義見http://activemq.apache.org/wildcards.html -->
<destinationPolicy>
<policyMap>
<policyEntries>
<!-- 這裡使用了wildcards,表示所有以EUCITA開頭的topic -->
<policyEntry topic="EUCITA.>" producerFlowControl="false" memoryLimit="10mb">
<!-- 分發策略 -->
<dispatchPolicy>
<!-- 按順序分發 -->
<strictOrderDispatchPolicy/>
</dispatchPolicy>
<!-- 恢複策略-->
<subscriptionRecoveryPolicy>
<!-- 只恢複最後一個message -->
<lastImageSubscriptionRecoveryPolicy/>
</subscriptionRecoveryPolicy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
<!-- The transport connectors ActiveMQ will listen to -->
<transportConnectors>
<transportConnector name="openwire" uri="tcp://192.168.1.148:61616" discoveryUri="multicast://default"/>
<transportConnector name="ssl" uri="ssl://192.168.1.148:61617"/>
<transportConnector name="stomp" uri="stomp://192.168.1.148:61613"/>
<transportConnector name="xmpp" uri="xmpp://192.168.1.148:61222"/>
</transportConnectors>
<!-- 訊息持久化方式 -->
<persistenceAdapter>
<amqPersistenceAdapter directory="${activemq.base}/data"/>
</persistenceAdapter>
</broker>
<!-- lets create a command agent to respond to message based admin commands on the ActiveMQ.Agent topic -->
<commandAgent xmlns="http://activemq.org/config/1.0"/>
<!-- An embedded servlet engine for serving up the Admin console -->
<jetty xmlns="http://mortbay.com/schemas/jetty/1.0">
<connectors>
<nioConnector port="8161" />
</connectors>
<handlers>
<webAppContext contextPath="/admin" resourceBase="${activemq.base}/webapps/admin" logUrlOnStart="true" />
<webAppContext contextPath="/demo" resourceBase="${activemq.base}/webapps/demo" logUrlOnStart="true" />
</handlers>
</jetty>
</beans>
4.ActiveMQ的安全驗證配置
ActiveMQ的安全性,主要表現在兩個方面,一個是它的JMS服務的安全性,一個是它的管理主控台的安全性。在預設情況下,ActiveMQ在這兩個方面都是沒有配置安全性的。
JMS服務安全性
A.如果沒有配置JMS服務的安全性,任何連入網路的人,只要知道ActiveMQ伺服器的具體地址(包括IP地址,連接埠,訊息地址[隊列或者主題地址]),就可以肆無忌憚的發送、接收訊息。那麼要解決JMS服務的安全性,最關鍵的問題就是解決身份認證的問題。
1)增加plugin
<plugins>
<!--use JAAS to authenticate using the login.config file on the classpath to configure JAAS -->
<jaasAuthenticationPlugin configuration="activemq-domain" />
<!-- lets configure a destination based authorization mechanism -->
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry queue=">" read="admins" write="admins" admin="admins" />
<authorizationEntry topic=">" read="admins" write="admins" admin="admins" />
<authorizationEntry queue="ActiveMQ.Advisory.>" read="admins" write="admins" admin="admins" />
<authorizationEntry topic="ActiveMQ.Advisory.>" read="admins" write="admins" admin="admins" />
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>
</plugins>
2)在conf中增加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";
};
3)在conf中增加groups.properties檔案,內容如下:
admins=system
4)在conf中增加users.properties檔案,內容如下:
system=manager
管理主控台安全性
ActiveMQ預設的管理是通過內建的jetty伺服器,只要在瀏覽器中輸入http://localhost:8161/admin,不需要登入,就可以對隊列、主題及訊息等進行管理,可以想象這非常不安全。那麼要解決管理主控台的安全性,除了通過修改管理連接埠號碼以及應用程式名稱之外,最關鍵的也是需要進行配置,必須通過身份認證才能登入。本文要說的方式主要是在不改變內建jetty的方式下,通過配置基本認證的方式來實現安全登入。
1)在activemq.xml中增加realm設定
<userRealms>
<jaasUserRealm name="adminRealm" loginModuleName="adminLoginModule">
</jaasUserRealm>
</userRealms>
2)修改login.config檔案,參考上面已見的login.config檔案
adminLoginModule {
org.mortbay.jetty.plus.jaas.spi.PropertyFileLoginModule required
debug="true"
file="C:/software/apache-activemq-5.2.0/conf/realm.properties";
};
3)在lib/web中增加jetty-plus-6.1.9.jar;
4)在conf中增加realm.properties檔案
system: MD5:1d0258c2440a8d19e716292b231e3190,admins
注意,上面是使用者名稱為system,而密碼為manager,如果使用者密碼不一致,請根據如下命令獲得相關資訊
java -cp jetty-6.1.9.jar;jetty-util-6.1.9.jar org.mortbay.jetty.security.Password system manager
5)修改webapps/admin/WEB-INF/web.xml
<security-constraint>
<web-resource-collection>
<web-resource-name>adminRealm</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admins</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>adminRealm</realm-name>
</login-config>
總結
經過如上配置後,必須通過使用者安全驗證才能串連上Message Service器並進行訊息發送和接收。當訪問http://localhost:8161/admin/時,將會快顯視窗以讓使用者輸入登入帳號和密碼。特別說明一下,ActiveMQ跟安全相關的有三個地方,分別是上面說的Message Service器本身的身份認證、Web Console的身份認證,還有一個地方是通過Web Console訪問Message Service器的身份認證,也就是conf/credentials.properties,檔案內容如下:
activemq.username=system
activemq.password=manager
這三個地方的使用者名稱密碼最好一致,尤其是credentials.properties中的帳號與users.properties中的管理員帳號必須一致。
三、 JMS的應用:
1. JMS應用程式介面
ConnectionFactory 介面(串連工廠)
使用者用來建立到JMS提供者的串連的被管對象。JMS客戶通過可移植的介面訪問串連,這樣當下層的實現改變時,代碼不需要進行修改。 管理員在JNDI名字空間中配置串連工廠,這樣,JMS客戶才能夠尋找到它們。根據訊息類型的不同,使用者將使用隊列串連工廠,或者主題串連工廠。
Connection 介面(串連)
串連代表了應用程式和Message Service器之間的通訊鏈路。在獲得了串連工廠後,就可以建立一個與JMS提供者的串連。根據不同的連線類型,串連允許使用者建立會話,以發送和接收隊列和主題到目標。
Destination 介面(目標)
目標是一個封裝了訊息目標標識符的被管對象,訊息目標是指訊息發布和接收的地點,或者是隊列,或者是主題。JMS管理員建立這些對象,然後使用者通過JNDI發現它們。和串連工廠一樣,管理員可以建立兩種類型的目標,點對點模型的隊列,以及發行者/訂閱者模型的主題。
MessageConsumer 介面(訊息消費者)
由會話建立的對象,用於接收發送到目標的訊息。消費者可以同步地(阻塞模式),或非同步(非阻塞)接收隊列和主題類型的訊息。
MessageProducer 介面(訊息生產者)
由會話建立的對象,用於發送訊息到目標。使用者可以建立某個目標的寄件者,也可以建立一個通用的寄件者,在發送訊息時指定目標。
Message 介面(訊息)
是在消費者和生產者之間傳送的對象,也就是說從一個應用程式創送到另一個應用程式。一個訊息有三個主要部分:
訊息頭(必須):包含用於識別和為訊息尋找路由的操作設定。
一組訊息屬性(可選):包含額外的屬性,支援其他提供者和使用者的相容。可以建立定製的欄位和過濾器(訊息選取器)。
一個訊息體(可選):允許使用者建立五種類型的訊息(簡訊,映射訊息,位元組訊息,流訊息和對象訊息)。
訊息介面非常靈活,並提供了許多方式來定製訊息的內容。
Session 介面(會話)
表示一個單線程的上下文,用於發送和接收訊息。由於會話是單線程的,所以訊息是連續的,就是說訊息是按照發送的順序一個一個接收的。會話的好處是它支援事務。如果使用者選擇了事務支援,會話上下文將儲存一組訊息,直到事務被提交才發送這些訊息。在提交事務之前,使用者可以使用復原操作取消這些訊息。一個會話允許使用者建立訊息生產者來發送訊息,建立訊息消費者來接收訊息。
四、 JMS封裝介面的應用:
(以下是自己封裝的介面)
Jms訊息管理類:JmsServiceManager.java
1. public void sendTextMessage (SmsConfig config1,String content)
是發送 “一般字元串訊息,包含一個String”的方法
參數說明:
A.String content :要發送的一般字元串訊息
B.SmsConfig config1 :是配置jms發送環境的bean類(常用的參數如下:)
1)/*訊息的transacted模式*/
private boolean transacted = false;
//jms事務要用到 預設是false 是否啟用本地事務
2) /**
* 傳遞模式, 有兩種模式: PERSISTENT(true) 和
* NON_PERSISTENT(false)
* 訊息的發送模式:persistent或nonpersistent。
* 前者表示訊息在被消費之前,如果JMS提供者DOWN了,
* 重新啟動後訊息仍然存在。
* 後者在這種情況下表示訊息會被丟失。
* */
private boolean durable = false; //預設是false
3) /**
* 訊息池d名字
* 發送訊息的目的地
* */
private String subject = "TOOL.DEFAULT";
//預設是TOOL.DEFAULT
4) /**
* 串連url
* URL的值為"failover:(tcp://localhost:61616)",
* 其中failover關鍵字表示用戶端與ActiveMQ Server的
* 串連中斷後(如ActiveMQ Server重新啟動),
* 用戶端會自動重新串連,不需要在我們的代碼中處理了。
* */
private String url = "failover:(tcp://localhost:61616)"; //預設是failover:(tcp://localhost:61616)
5) /**
* 發送訊息或接收訊息時使用的使用者名稱
* */
private String pwd = "system";
//預設是system
6) /**
* 發送訊息或接收訊息時使用的密碼
* */
private String user = "manager";
//預設是manager
7) /**
* 點對點傳輸模式或訂閱模式 true 訂閱模式,false 點對點模式
* */
private boolean topic = false; //預設是false
8)/**
* 表示一個訊息的有效期間。只有在這個有效期間內,
*訊息消費者才可以消費這個訊息。
* 預設值為0,表示訊息永不到期。
* 可以通過下面的方式設定:有效期間1小時 (1000毫秒 * 60秒 * 60分)
* */
private long timeToLive = 0; //預設值為0,表示訊息永不到期。
9)/**
* 表示接收到訊息後返回的目的地
* */
private String replyTo = "";
//預設值為空白,表示接收到訊息不返回。
2. public void sendObjectMessage(SmsConfig config1,Object object)
是發送 “對象訊息,包含一個可序列化的Java 對象”的方法
參數說明:
Object object : 是繼承了com.jms.tool. ActionBean
SmsConfig config1 : 和 1 方法中的參數是一樣的
3. public void sessionCommit()
//提交事務
4. public void closeSessionAndConnection()
//關閉會話和串連
5. public void sessionRollback()
//交易回復
6. public void reciveMessage(SmsConfig config1)
是發送 “接受監聽資訊”的方法
參數說明:
SmsConfig config1 : 和 1 方法中的參數是一樣的
Jms訊息接受處理類:JmsReciveAction.java
1. public static void actionObjectMessage(Object object)
是“接受監聽資訊(對象訊息)”的處理方法
參數說明:
Object object : 是接收到的訊息對象
2. public static String actionTextMessage(String content)
是“接受監聽資訊(String訊息)”的處理方法
參數說明:
String content : 是接收到的String訊息
並返回處理後的結果。(如果沒有返回結果可以返回Null 字元串””)
調用Jms介面例子:
1.發送”String訊息”
public static void senderText(){
JmsServiceManager jmsManager = new JmsServiceManager();
SmsConfig config = new SmsConfig();//jms環境配置bean
config.setTopic(false); //是 點對點 還是 發布訂閱 發送模式
config.setDurable(true);
config.setSubject("127.0.0.1"); //訊息發送目的地
config.setTransacted(true);//是否啟用本地事務
config.setUrl("failover:(tcp://localhost:61616)");
//如果ActiveMQ設定了安全驗證 還需要設定使用者名稱和密碼
config.setUser("system");
config.setPwd("manager");
try{
jmsManager.sendTextMessage(config, "我是中國人,我愛我的祖國"); jmsManager.sessionCommit(); //提交事務
}catch (Exception e) {
e.printStackTrace();
jmsManager.sessionRollback();//交易回復
}finally{
jmsManager.closeSessionAndConnection(); //關閉會話和串連
}
}
註:如果config.setTransacted(false);//是否啟用本地事務 則不用提交事務
2.接收訊息
public static void reciverText(){
JmsServiceManager jmsManager = new JmsServiceManager();
SmsConfig config = new SmsConfig();
config.setTopic(false);
//config.setDurable(true);
config.setSubject("127.0.0.1"); //訊息接收目的地
config.setUrl("failover:(tcp://localhost:61616)");
//如果ActiveMQ設定了安全驗證 還需要設定使用者名稱和密碼
config.setUser("system");
config.setPwd("manager");
jmsManager.reciveMessage(config);
}