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 ( ) ;
}
}
} ) ;