1. JMS簡介
JMS全稱:Java Message Service ,中文:Java 消息服務,是 Java 的一套 API 標準
最初的目的是爲了使應用程序能夠訪問現有的 MOM系
MOM:Message Oriented Middleware,即消息中間件,它可以利用高效可靠的消息傳遞機 制進行平臺無關的數據交流,並基於數據通信來進行分佈式系統的集成
常見 MOM 系統包括 Apache 的 ActiveMQ、 阿里巴巴的 RocketMQ、IBM 的 MQSeries、Microsoft 的 MSMQ、BEA 的 RabbitMQ 等。並非全部的 MOM 系統都遵循 JMS 規範,也就是並非全提供了JMS實現,提供了JMS 實現的 MOM,又被稱爲 JMSProvider
JMS與MOM的關係類似JDBC和數據庫之間的關係
2 消息中間件的應用場景
異步通信:消息隊列提供了異步處理機制,允許用戶把一個消息放入隊列,但並不立即處理它
過載保護:不將請求直接發送給服務端,而是由服務端自己來取,這樣防止大量請求同時到達服務端,使整個系統崩潰
解耦:A和B直接相連時,一旦B死掉,那麼A的功能也都不好用了,採用消息中間件解耦,可以保證B死掉,A也能正常發送給隊列,當B復活後,又可以繼續完成之前隊列中的任務
消息通訊:客戶端A,客戶端B,客戶端N訂閱同一主題,進行消息發佈和接收。實現類似聊天室效果
順序保證:在大多使用場景下,數據處理的順序都很重要。大部分消息隊列本來就是排序的,並且能保證數據會按照特定的順序來處理。
數據流處理:分佈式系統產生的海量數據流,如:業務日誌、監控數據、用戶行爲等,針對這些數據流進行實時或批量採集彙總,然後進行大數據分析是當前互聯網的必備技術,通過消息隊列完成此類數據收集是最好的選擇
擴展性:消息中間件可以很容易橫向擴容
3 常用消息隊列比較
特性MQ
ActiveMQ
RabbitMQ
RocketMQ
Kafka
生產者消費者模式
支持
支持
支持
支持
發佈訂閱模式
支持
支持
支持
支持
請求迴應模式
支持
支持
不支持
不支持
Api完備性
高
高
高
高
多語言支持
支持
支持
java
支持
單機吞吐量
萬級
萬級
萬級
十萬級
消息延遲
無
微秒級
毫秒級
毫秒級
可用性
高(主從)
高(主從)
非常高(分佈式)
非常高(分佈式)
消息丟失
低
低
理論上不會丟失
理論上不會丟失
文檔的完備性
高
高
高
高
提供快速入門
有
有
有
有
社區活躍度
高
高
有
高
商業支持
無
無
商業雲
商業雲
4 JMS中的角色
Broker:消息服務器,相當於server,提供消息核心服務
Provider:消息生產者,是由會話創建的一個對象,用於把消息發送到一個目的地
Consumer:消息消費者,是由會話創建的一個對象,它用於接收發送到目的地的消息
消費消息的兩種方式:
同步消費:調用消費者的receive方法,從目的地中顯式提取消息。receive方法可以一直阻塞到消息到達
異步消費:客戶端可以爲消費者註冊一個消息監聽器,以定義在消息到達時所採取的動作
p2p:基於點對點的消息模型
消息生產者生產消息發送到 queue 中,然後消息消費者從 queue 中取出並且消費消息
消息被消費以後,queue 中不再有存儲,所以消息消費者不可能消費到已經被消費的消息
Queue 支持存在多個消費者,但是對一個消息而言,只會有一個消費者可以消費、其它 的則不能消費此消息了
當消費者不存在時,消息會一直保存,直到有消費消費
pub/sub:基於訂閱/發佈的消息模型
消息生產者(發佈)將消息發佈到 topic 中,同時有多個消息消費者(訂閱)消費該消
息
和點對點方式不同,發佈到 topic 的消息會被所有訂閱者消費
當生產者發佈消息,不管是否有消費者。都不會保存消息 一定要先有消息的消費者,後有消息的生產者
PTP 和 PUB/SUB 簡單對比
1
Topic
Queue
Publish Subscribe messaging 發佈 訂閱消息
Point-to-Point 點對點
有無狀態
topic 數據默認不落地,是無狀態的,也就是發消息時,如果接收的人不在線,那麼該消息他就收不到了。
Queue 數據默認會在 mq 服 務器上以文件形式保存,比如 Active MQ 一 般 保 存 在 $AMQ_HOME\data\kahadb 下 面。也可以配置成 DB 存儲。
完整性保障
並不保證 publisher 發佈的每條數 據,Subscriber 都能接受到。
Queue 保證每條數據都能 被 receiver 接收。消息不超時。
消息是否會丟失
一般來說 publisher 發佈消息到某 一個 topic 時,只有正在監聽該 topic 地址的 sub 能夠接收到消息;如果沒 有 sub 在監聽,該 topic 就丟失了。
Sender 發 送 消 息 到 目 標 Queue, receiver 可以異步接收這 個 Queue 上的消息。Queue 上的 消息如果暫時沒有 receiver 來 取,也不會丟失。前提是消息不 超時。
消息發佈接 收策略
一對多的消息發佈接收策略,監 聽同一個topic地址的多個sub都能收 到 publisher 發送的消息。Sub 接收完 通知 mq 服務器
一對一的消息發佈接收策 略,一個 sender 發送的消息,只 能有一個 receiver 接收。 receiver 接收完後,通知 mq 服務器已接 收,mq 服務器對 queue 裏的消 息採取刪除或其他操作。
Queue:隊列存儲,常用與點對點消息模型 ,默認只能由唯一的一個消費者處理。一旦處理消息刪除
Topic:主題存儲,用於訂閱/發佈消息模型主題中的消息,會發送給所有的消費者同時處理。只有在消息可以重複處 理的業務場景中可使用。Queue/Topic都是 Destination 的子接口
ConnectionFactory :連接工廠,jms中用它創建連接。連接工廠是客戶用來創建連接的對象,例如ActiveMQ提供的ActiveMQConnectionFactory
Connection:JMS Connection封裝了客戶與JMS提供者之間的一個虛擬的連接
Destination:消息的目的地。在點對點消息傳遞域中,目的地被成爲隊列(queue);在發佈/訂閱消息傳遞域中,目的地被成爲主題(topic)
Session:JMS Session是生產和消費消息的一個單線程上下文。會話用於創建消息生產者(producer)、消息消費者(consumer)和消息(message)等。會話提供了一個事務性的上下文,在這個上下文中,一組發送和接收被組合到了一個原子操作中
5 JMS的消息格式與類型
5.1 格式
消息頭
消息屬性
消息體
5.2 類型
5.2.1 TextMessage:文本消息
5.2.2 MapMessage:k-v
發送端
MapMessage mapMessage = session. createMapMessage ( ) ;
mapMessage. setString ( "name" , "lucy" ) ;
mapMessage. setBoolean ( "yihun" , false ) ;
mapMessage. setInt ( "age" , 17 ) ;
producer. send ( mapMessage) ;
接收端
Message message = consumer. receive ( ) ;
MapMessage mes = ( MapMessage) message;
System. out. println ( mes) ;
System. out. println ( mes. getString ( "name" ) ) ;
5.2.3 BytesMessage:字節流,一般用於傳輸小文件、圖片
發送端
BytesMessage bytesMessage = session. createBytesMessage ( ) ;
bytesMessage. writeBytes ( "str" . getBytes ( ) ) ;
bytesMessage. writeUTF ( "哈哈" ) ;
接收端
if ( message instanceof BytesMessage ) {
BytesMessage bm = ( BytesMessage) message;
byte [ ] b = new byte [ 1024 ] ;
int len = - 1 ;
while ( ( len = bm. readBytes ( b) ) != - 1 ) {
System. out. println ( new String ( b, 0 , len) ) ;
}
}
bm. readBoolean ( ) ;
bm. readUTF ( ) ;
5.2.4 StreamMessage:java原始的數據流
5.2.5 ObjectMessage:序列化的java對象
發送端
connectionFactory. setTrustedPackages (
new ArrayList < String> ( Arrays. asList ( new String [ ] { Girl. class . getPackage ( ) . getName ( ) } ) ) ) ;
Girl girl = new Girl ( "qiqi" , 25 , 398.0 ) ;
Message message = session. createObjectMessage ( girl) ;
接收端
if ( message instanceof ActiveMQObjectMessage ) {
Girl girl = ( Girl) ( ( ActiveMQObjectMessage) message) . getObject ( ) ;
System. out. println ( girl) ;
System. out. println ( girl. getName ( ) ) ;
}
6 消息的特性
6.1 持久性
持久化消息後,即使ActiveMQ宕機,消息也不會消失,消息被消費者消費掉後,數據庫中內容纔會消失
MQ接受消息,還需要向數據庫中寫,會影響效率,所以不建議使用大數據庫,而是推薦使用kahadb這種小型數據庫,速度非常快
生產環境幾乎不可能用mysql或oracle進行消息持久化存儲,此處用oracle是爲了方便觀察消息在數據庫中的存儲形式
JMS中的持久化
producer. setDeliveryMode ( DeliveryMode. PERSISTENT) ;
6.1.1 KahaDB存儲
KahaDB是默認的持久化策略,所有消息順序添加到一個日誌文件中,同時另外有一個索引文件記錄指向這些日誌的存儲地址,還有一個事務日誌用於消息回覆操作。是一個專門針對消息持久化的解決方案,它對典型的消息使用模式進行了優化
在data/kahadb這個目錄下,會生成四個文件,來完成消息持久化
db.data:消息的索引文件,本質上是B-Tree(B樹),使用B-Tree作爲索引指向db-*.log裏面存儲的消息
db.redo:用來進行消息恢復
db-.log:存儲消息內容。新的數據以APPEND的方式追加到日誌文件末尾。屬於順序寫入,因此消息存儲是比較 快的。默認是32M,達到閥值會自動遞增
lock:鎖,寫入當前獲得kahadb讀寫權限的broker ,用於在集羣環境下的競爭處理
配置
< persistenceAdapter>
< ! -- directory: 保存數據的目錄; journalMaxFileLength: 保存消息的文件大小,是每個數據文件大小,超過後滾動 -- >
< kahaDBdirectory= "${activemq.data}/kahadb" journalMaxFileLength= "16mb" / > < / persistenceAdapter>
特性
日誌形式存儲消息
消息索引以 B-Tree 結構存儲,可以快速更新
完全支持 JMS 事務
支持多種恢復機制
6.1.2 JDBC存儲
activemq.xml
< bean id = " oracle-ds" class = " org.apache.commons.dbcp.BasicDataSource" destroy-method = " close" >
< property name = " driverClassName" value = " oracle.jdbc.OracleDriver" />
< property name = " url" value = " jdbc:oracle:thin:@192.168.15.110:1521:fcrhost" />
< property name = " username" value = " c50hst" />
< property name = " password" value = " c50hst" />
< property name = " maxActive" value = " 200" />
< property name = " poolPreparedStatements" value = " true" />
</ bean>
...
< persistenceAdapter>
< jdbcPersistenceAdapter dataSource = " #oracle-ds" createTablesOnStartup = " true" />
</ persistenceAdapter>
數據庫連接池和數據庫jdbc連接,需要依賴如下jar包
commons-dbcp-1.4.jar
commons-pool-1.6.jar
ojdbc6.jar
使用JDBC持久化方式,數據庫默認會創建3個表
activemq_msgs:用於存儲消息,Queue和Topic都存儲在這個表中
id:自增的數據庫主鍵
container:消息的destination
msgid_prod:消息發送者客戶端的主鍵
msg_seq:是發送消息的順序,msgid_prod+ msg_seq可以組成jms的messageid
expiration:消息的過期時間,存儲的是從1970 - 01 - 01 到現在的毫秒數
msg:消息本體的java序列化對象的二進制數據
priority:優先級,從0 - 9 ,數值越大優先級越高
xid: 用於存儲訂閱關係。如果是持久化topic,訂閱者和服務器的訂閱關係在這個表保存。
activemq_acks:用於存儲訂閱關係。如果是持久化Topic,訂閱者和服務器的訂閱關係在這個表保存
container:消息的destination
sub_dest:如果是使用static 集羣,這個字段會有集羣其他系統的信息
client_id:每個訂閱者都必須有一個唯一的客戶端id用以區分
sub_name:訂閱者名稱
selector:選擇器,可以選擇只消費滿足條件的消息。條件可以用自定義屬性實現,可支持多屬性and和or操作
last_acked_id:記錄消費過的消息的id。
activemq_lock:在集羣環境中才有用,只有一個Broker可以獲得消息,稱爲Master Broker,其他的只能作爲備份等待Master Broker不可用,纔可能成爲下一個Master Broker。這個表用於記錄哪個Broker是當前的Master Broker
6.1.3 JDBC Message store with ActiveMQ Journal
消息中間件收到消息後,向內存中存儲消息同時,往文件裏寫,然後就返回ack
此時有consumer來消費,就會從文件中刪除
如果指定時間內未被消費,會通過jdbc寫入數據庫,而且是批量地寫和刪(不是單條),減少寫入數據庫中的數據量
6.2 本地事務
在一個JMS客戶端,可以使用本地事務來組合消息的發送和接收
可以在創建session時,指定開啓事務
當事務設置爲true,應答模式默認只能是Session.SESSION_TRANSACTED,即使設置爲其他的,也不會生效,當設置爲其他值時,手動調用message.acknowledge(),會和producer.send類似,只要沒最終通過session.commit提交事務,就無法通知ActiveMQ該消息被確認
開啓事務可以避免頻繁發送消息造成的網絡連接問題,也可以在出現問題時,及時回滾
Session session = connection. createSession ( true , Session. AUTO_ACKNOWLEDGE) ;
6.3 可靠性
消息的成功消費通常包含三個階段:客戶接收消息、客戶處理消息和消息被確認
消息隊列收到確認包後,纔會將該消息從消息隊列中移除(如果持久化消息,會從數據庫中移除)
在一個session中consumer接收到的消息,在另一個consumer中無法重複接收該消息,如果第一個客戶端始終沒確認消息,且最後該客戶端對應session斷開,那麼消息會重新投遞給第二個客戶端
在事務性會話中,當一個事務被提交的時候,確認自動發生
在非事務性會話中,消息何時被確認取決於創建會話時的簽收模式(acknowledgement mode),acknowledgement其實就是ack,也就是確認包
Session session = connection. createSession ( false , Session. AUTO_ACKNOWLEDGE) ;
6.3 優先級
可以設置消息優先級,這樣就可以不按發送消息的順序去消費
優先級分10個級別,從0(最低)到9(最高)。如果不指定優先級,默認級別是4
開啓優先級:activemq.xml
< policyEntry queue = " queue1" prioritizedMessages = " true" />
設
producer. setPriority ( 9 ) ;
producer. send ( textMessage, DeliveryMode. PERSISTENT, 9 , 1000 * 100 ) ;
7 HelloWorld
下載ActiveMQ
http: / / activemq. apache. org/
啓動ActiveMQ
bin/ win64/ activemq. bat
進入管理界面
http: / / localhost: 8161 /
修改訪問端口
conf/ jetty. xml
創建maven項目
會自動幫你下載、並引入需要的activemq-all-5.15.12.jar以及源碼,如果創建正常項目,需要將E:\Program Files (x86)\apache-activemq-5.15.12\下的該jar包引入
File–new–Project–Maven Project–選中create a simple project–填寫Goup Id(com.mashibing.mq)與Artifact ID(activemq02)
修改pom.xml文件
< project xmlns= "http://maven.apache.org/POM/4.0.0"
xmlns: xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi: schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" >
< modelVersion> 4.0 .0 < / modelVersion>
< groupId> com. mashibing. mq< / groupId>
< artifactId> activemq02< / artifactId>
< version> 0.0 .1 - SNAPSHOT< / version>
< ! -- https: / / mvnrepository. com/ artifact/ org. apache. activemq/ activemq- all -- >
< dependencies>
< dependency>
< groupId> org. apache. activemq< / groupId>
< artifactId> activemq- all< / artifactId>
< version> 5.15 .12 < / version>
< / dependency>
< / dependencies>
< / project>
Sender
package com. mashibing. mq;
import javax. jms. Connection;
import javax. jms. DeliveryMode;
import javax. jms. MessageProducer;
import javax. jms. Queue;
import javax. jms. Session;
import javax. jms. TextMessage;
import org. apache. activemq. ActiveMQConnectionFactory;
public class Sender {
public static void main ( String[ ] args) throws Exception{
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory (
"admin" ,
"admin" ,
"tcp://localhost:61616"
) ;
Connection connection = connectionFactory. createConnection ( ) ;
Session session = connection. createSession ( false , Session. AUTO_ACKNOWLEDGE) ;
Queue queue = session. createQueue ( "user" ) ;
MessageProducer producer = session. createProducer ( queue) ;
for ( int i = 0 ; i < 1000 ; i++ ) {
TextMessage textMessage = session. createTextMessage ( "hi: " + i) ;
if ( i % 4 == 0 ) {
textMessage. setJMSPriority ( 9 ) ;
}
producer. send ( textMessage) ;
Thread. sleep ( 3000 ) ;
}
connection. close ( ) ;
System. out. println ( "System exit...." ) ;
}
}
Receiver
package com. mashibing. activemq01;
import javax. jms. Connection;
import javax. jms. Destination;
import javax. jms. JMSException;
import javax. jms. Message;
import javax. jms. MessageConsumer;
import javax. jms. MessageProducer;
import javax. jms. Queue;
import javax. jms. Session;
import javax. jms. TextMessage;
import org. apache. activemq. ActiveMQConnectionFactory;
public class Receiver {
public static void main ( String[ ] args) throws Exception {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory (
ActiveMQConnectionFactory. DEFAULT_USER,
ActiveMQConnectionFactory. DEFAULT_PASSWORD,
"tcp://localhost:61616"
) ;
Connection connection = activeMQConnectionFactory. createConnection ( ) ;
connection. start ( ) ;
Session session = connection. createSession ( false , Session. AUTO_ACKNOWLEDGE) ;
Queue queue = session. createQueue ( "f" ) ;
MessageConsumer consumer = session. createConsumer ( queue) ;
while ( true ) {
TextMessage receive = ( TextMessage) consumer. receive ( ) ;
System. out. println ( "TextMessage:" + receive. getText ( ) ) ;
}
}
}
8 Active MQ的安全機制
web控制檯安全:重啓後生效
conf/ jetty- realm. properties
admin: admin, admin
user: user, user
消息安全機制:配置建立連接工廠時的用戶和密碼
conf/ activemq. xml
< plugins>
< simpleAuthenticationPlugin>
< users>
< authenticationUser username = " admin" password = " admin" groups = " admins,publishers,consumers" />
< authenticationUser username = " publisher" password = " publisher" groups = " publishers,consumers" />
< authenticationUser username = " consumer" password = " consumer" groups = " consumers" />
< authenticationUser username = " guest" password = " guest" groups = " guests" />
</ users>
</ simpleAuthenticationPlugin>
</ plugins>
9 消息超時/過期
消息未被消費時,會存放於內存中,長期堆積存在內存裝不下的風險,爲防止這種情況產生,通常可以設置消息的超時時間
producer. setTimeToLive ( 1000 ) ;
ActiveMQ會隔一段時間、或在消費端嘗試訪問某隊列中消息時,檢查該隊列中消息是否超時,如果超時,不會讓消費者消費此消息,而是讓死信隊列消費該消息,同時自動將該消息放入死信隊列
9.1 死信隊列
死信隊列保存一些因爲業務邏輯處理失敗,而導致消息的失敗或者說是消息發送過期的消息,有了死信隊列能夠保證在發送消息和接收消息過程中因爲某些異常導致消息丟失的隊列
由於非持久化消息,系統可能認爲這些消息並不重要,丟不丟失無所謂,默認情況下,不會進入死信隊列
對於不進死信隊列的消息,超時候,控制檯上會發現該消息被莫名其妙消費
死信隊列和正常隊列功能相同,本質上就是一個默認名爲ActiveMQ.DLQ的隊列,客戶端同樣可以從該隊列中獲取消息
修改死信隊列名稱與非持久化消息進入死信隊列
< policyEntry queue = " user" prioritizedMessages = " true" >
< deadLetterStrategy>
< individualDeadLetterStrategy queuePrefix = " DLxxQ." useQueueForQueueMessages = " true" processNonPersistent = " true" />
</ deadLetterStrategy>
</ policyEntry>
不讓消息進入死信隊列
< individualDeadLetterStrategy processExpired = " false" />
10 獨佔消費者
創建Queue或Topic時,可以設置,其創建出的消費者,必須獨佔這個隊列中的所有消息
也就是說,當這個消費者開始消費這個隊列中的消息,只要這個消費者沒掛掉,剩下所有消息,必須都由這個消費者來消費, 其他消費者無法消費到該消息
Queue queue = session. createQueue ( "xxoo?consumer.exclusive=true" ) ;
Queue queue = session. createQueue ( "xxoo?consumer.exclusive=true&consumer.priority=10" ) ;
使用selector
可以爲消息分組,同時設定consumer只消費某組下的消息
可以達到定向分發、消費消息,也就是負載均衡的感覺
如果我們已知一個服務處理消息的速度,我們就可以動態的去調整給每個服務器發多少消息
textMessage. setLongProperty ( "week" , i% 7 ) ;
MessageConsumer consumer = session. createConsumer ( queue, "week=1" ) ;
11 消息發送原理
同步發送:調用send 方法發送消息時,該方法一直阻塞,直到ActiveMQ確認消息已經存儲在持久性數據存儲中,併發回確認消息
異步發送時,調用的是ActiveMQSession中send方法中的this.connection.asyncSendPacket(msg),而同步發送,調用的是其下面的this.connection.syncSendPacket(msg,sendTimeout)
開啓事務
關閉事務
持久化
異步
同步
非持久化
異步
異步
注意send只是阻塞到mq將數據都存放到數據庫中,而不是阻塞到consumer處理完消息
可以用以下幾種方式設置爲異步發送
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory ( "admin" , "admin" ,
"tcp://localhost:61616?jms.useAsyncSend=true" ) ;
( ( ActiveMQConnectionFactory) connectionFactory) . setUseAsyncSend ( true ) ;
( ( ActiveMQConnection) connection) . setUseAsyncSend ( true )
異步發送消息是有消息丟失的風險的
設置異步發送時的windowSize
用來約束在異步發送時,producer端允許積壓的(尚未ACK)的消息的尺寸,
只對異步發送有意義,因爲同步發送根本不會積壓ACK
每次發送消息之後,都將會導致memoryUsage尺寸增加(+message.size),當broker返回producerAck時,memoryUsage尺寸減少producerAck.size,此size表示先前發送消息的大小
發送消息時,會檢測memoryUsage中是否還有足夠空間,如果足夠,正常發送,如果不足,將會阻塞
可以通過如下2種方式設置
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory ( "admin" , "admin" ,
"tcp://localhost:61616?jms.producerWindowSize=1048576" ) ;
Queue queue = session. createQueue ( "user?producer.windowSize=1048576" ) ;
12 延遲消息投遞
消息發送給消息中間件,然後消息中間件決定這個信息多久後才生效
需要在配置文件中開啓延遲和調度
< broker xmlns = " http://activemq.apache.org/schema/core" brokerName = " localhost" dataDirectory = " ${activemq.data}" schedulerSupport = " true" >
編程時設置消息的屬性爲延遲投遞
message. setLongProperty ( ScheduledMessage. AMQ_SCHEDULED_DELAY, 10 * 1000 ) ;
帶間隔的重複發送
long delay = 10 * 1000 ;
long period = 2 * 1000 ;
int repeat = 9 ;
message. setLongProperty ( ScheduledMessage. AMQ_SCHEDULED_DELAY, delay) ;
message. setLongProperty ( ScheduledMessage. AMQ_SCHEDULED_PERIOD, period) ;
message. setIntProperty ( ScheduledMessage. AMQ_SCHEDULED_REPEAT, repeat) ;
createProducer. send ( message) ;
13 Cron表達式指定時間發送消息
Cron表達式是一個字符串,字符串以5或6個空格隔開,分爲6或7個域,每一個域代表一個含義,Cron有如下兩種語法格式
Seconds Minutes Hours DayofMonth Month DayofWeek Year
Seconds Minutes Hours DayofMonth Month DayofWeek
每一個域可出現的字符如下
Seconds:可出現", - * /"四個字符,有效範圍爲0-59的整數
Minutes:可出現", - * /"四個字符,有效範圍爲0-59的整數
Hours:可出現", - * /"四個字符,有效範圍爲0-23的整數
DayofMonth:可出現", - * / ? L W C"八個字符,有效範圍爲0-31的整數
Month:可出現", - * /"四個字符,有效範圍爲1-12的整數或JAN-DEc
DayofWeek:可出現", - * / ? L C #"四個字符,有效範圍爲1-7的整數或SUN-SAT兩個範圍。1表示星期天,2表示星期一, 依次類推
Year:可出現", - * /"四個字符,有效範圍爲1970-2099年
特殊字符的含義
:表示匹配該域的任意值,假如在Minutes域使用 , 即表示每分鐘都會觸發事件
?:只能用在DayofMonth和DayofWeek兩個域。它也匹配域的任意值,但實際不會。因爲DayofMonth和 DayofWeek會相互影響。例如想在每月的20日觸發調度,不管20日到底是星期幾,則只能使用如下寫法: 13 13 15 20 * ?, 其中最後一位只能用?,而不能使用*,如果使用*表示不管星期幾都會觸發,實際上並不是這樣
-:表示範圍,例如在Minutes域使用5-20,表示從5分到20分鐘每分鐘觸發一次
/:表示起始時間開始觸發,然後每隔固定時間觸發一次,例如在Minutes域使用5/20,則意味着5分鐘觸發一次,而25,45等分別觸發一次
,:表示列出枚舉值值。例如:在Minutes域使用5,20,則意味着在5和20分每分鐘觸發一次
L:表示最後,只能出現在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最後的一個星期四觸發
W:表示有效工作日(週一到週五),只能出現在DayofMonth域,系統將在離指定日期的最近的有效工作日觸發事件。例如:在 DayofMonth使用5W,如果5日是星期六,則將在最近的工作日:星期五,即4日觸發。如果5日是星期天,則在6日(週一)觸發;如果5日在星期一 到星期五中的一天,則就在5日觸發。另外一點,W的最近尋找不會跨過月份
LW:這兩個字符可以連用,表示在某個月最後一個工作日,即最後一個星期五
#:用於確定每個月第幾個星期幾,只能出現在DayofMonth域。例如在4#2,表示某月的第二個星期三
舉例
"0 0 12 * * ?"
"0 15 10 ? * *"
"0 15 10 * * ?"
"0 15 10 * * ? *"
"0 15 10 * * ? 2005"
"0 * 14 * * ?"
"0 0/5 14 * * ?"
"0 0/5 14,18 * * ?"
"0 0-5 14 * * ?"
"0 10,44 14 ? 3 WED"
"0 15 10 ? * MON-FRI"
"0 15 10 15 * ?"
"0 15 10 L * ?"
"0 15 10 ? * 6L"
"0 15 10 ? * 6L 2002-2005"
"0 15 10 ? * 6#3"
14 activemq.xml中的memoryUsage
< memoryUsage>
< memoryUsage percentOfJvmHeap = " 70" />
</ memoryUsage>
< storeUsage>
< storeUsage limit = " 100 gb" />
</ storeUsage>
< tempUsage>
< tempUsage limit = " 50 gb" />
</ tempUsage>
15 監聽器
使用for(true)接收消息時
如果一條消息特別大,會接收很久,receive方法會一直阻塞
for循環中的業務處理邏輯,如果需要處理很久,其他消息也同樣無法進來
無法高併發處理
可以使用監聽器來處理消息接收,從而併發處理消息,當收到消息後會調用onMessage方法對消息進行業務處理,就不用手動調用consumer.receive來接收消息了
consumer. setMessageListener ( new MessageListener ( ) {
public void onMessage ( Message message) {
try {
System. out. println ( "message2:" + ( ( TextMessage) message) . getText ( ) ) ;
} catch ( JMSException e) {
e. printStackTrace ( ) ;
}
}
} ) ;