消息转发模式
ActiveMQ支持两种消息转发模式:PERSISTENT(持久化)和NON_PERSISTENT(非持久化)消息。ActiveMQ默认采用的是PERSISTENT模式,消息在发送到ActiveMQ服务端后被持久化(持久化方案有多种,比如JDBC、AMQ、KahaDB、LevelDB等),如果消息服务器出现故障,可以恢复此消息并将消息传送至消费者,这种模式保证消息只被传送一次和成功使用一次。而NON_PERSISTENT模式保证消息最多被传送一次,不要求消息持久化存储,加入消息服务器出现故障,不保证消息不丢失。PERSISTENT模式有且只有一次转发消息,而NON_PERSISTENT模式最多一次转发消息。PERSISTENT消息不会丢失,并且不会被转发两次,而NON_PERSISTENT消息可能会丢失,但不会被转发两次。
在ActiveMQ中设置转发模式有两种方式,一是使用setDeliveryMode()方法,所有消息都采用这种传送模式,二是使用send()方法,为每一条消息设置传送模式。这两种方式对应javax.jms.MessageProducer接口中的三个方法:
void setDeliveryMode(int deliveryMode) throws JMSException;
void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException;
void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) thrwos JMSException;
使用PERSISTENT模式,表示业务场景更看重可靠性,使用NON_PERSISTENT模式,表示业务场景更看重性能。因为NON_PERSISTENT模式下,发送消息是异步的,Producer不需要等待Consumer的receipt消息,而在PERSISTENT模式下,传递消息需要先把消息存储起来,然后再传递。
消息积压
如果发送的下次没有及时消费掉,会导致消息不断积压得不到释放,从而阻塞消息队列。对于这种情况,可以配置消息过期时间和死信队列处理来预防。
消息过期
默认情况下,ActiveMQ的消息永不过期,如果业务场景需要,可以指定消息的过期时间。指定过期时间有两种方式,一是使用setTimeToLive()方法,二是使用send()方法。这两种方式对应javax.jms.MessageProducer接口中的三个方法:
void setTimeToLive(long timeToLive) throws JMSException;
void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException;
void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) thrwos JMSException;
如果设置timeToLive为0,表示永不过期。消息发送后,在消息过期时间到达,还没有被发送到目的地,该消息会被清除。
死信队列
一般情况下,消费消息有两种方式:一是调用MessageConsumer的receive()方法,该方法是阻塞的,接收到消息前会一直阻塞,消息返回给方法调用者后自动确认,二是通过MessageListener接口注册回调函数,有消息到达时,ActiveMQ调用接口的onMessage()方法,该方法执行完毕后消息会被确认,如果在onMessage()方法中抛出异常就不会被确认。如果该消息只有一个消费者,那么消息又会被重新获取继续抛出异常,ActiveMQ重试6次后,将这条消息丢入死信队列。
消息事务
ActiveMQ支持两种事务:JMS Transaction和XA Transaction。
JMS Transaction:使用Session接口的commit()和rollback()方法控制。
XA Transaction:为了支持两阶段提交协议,通过使用XASession与消息服务器通信来充当XAResource。
消息应答模式
对于消息消费者,除了可以使用事务的方式告知Broker消息已经处理完,更常用的是设置消息的应答模式,在javax.jms.Session接口中预定义了四种模式:
static final int SESSION_TRANSACTION = 0;// 会话事务
static final int AUTO_ACKONWLEDGE = 1;// 自动签收,当消费者通过receive()方法或者onMessage()方法成功返回消息后,session自动签收这条消息,表示消费者对消息的处理是成功的
static final int CLIENT_ACKNOWLEDGE = 2;// 客户端手动确认,必须显示调用javax.jms.Message接口中的acknowledge()方法签收消息,否则这条消息对ActiveMQ来说没有处理成功
static final int DUPS_OK_ACKNOWLEDGE = 3;// 自动批量确认,消费者按照一定策略想broker发送ack标识,表示一批消息处理完成,这种模式可能会引起消息的重复,但是可以降低Session的开销
设置消息应答模式一般是在调用Connection接口的createSession()方法创建会话对象时作为参数传进去的。
Session createSession(boolean transacted, int acknowledgeMode) throws JMSException;
一般情况下优先考虑AUTO_ACKNOWLEDGE模式,作用是延迟确认,消费者在处理完消息后不会发送ack标识,而是会缓存在Session中,等消息数量达到一定阈值,再发送一个ack指令告知一批消息已经处理完成。
消息发送优化
异步发送
ActiveMQ默认就是以异步模式发送消息的。例外情况是,在没有使用事务时,生产者以PERSISTENT模式传送消息,此时send方法是同步的,它会一直阻塞到ActiveMQ发回确认消息,并确认消息已经被存储。为了保证消息不会丢失,但会造成生产者阻塞。
生产者流量控制
如果消息积压并超过了限制大小,当生产者继续发送消息时,ActiveMQ会让生产者进入等待状态或直接抛出JMSException。
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntryqueue=">" producerFlowControl="true" memoryLimit="1OOmb">
</policyEnttries>
</policyMap>
</destinationPolicy>
以上配置表示为所有Queue模式的队列启用生产者流量控制,限制每个队列最大存储为100MB。
消息消费优化
消息预取
为了提高消息分发效率,ActiveMQ引入了消息预取(prefetch)机制,Broker在没有收到消费者的消息反馈前会继续发新消息给它,除非消费者的消息缓存区满了,或者未收到反馈的消息数量达到了预取数量上限值,这个值是prefetchSize,对于不同类型的队列,ActiveMQ默认预取数量也不同。
转发模式 | 队列类型 | prefetchSize |
PERSISTENT | Queue | 1000 |
NON_PERSISTENT | Queue | 1000 |
PERSISTENT | Topic | 1000 |
NON_PERSISTENT | Topic | 32766 |
慢速消费者
快速生产者生产的消息不能被Consumer及时消费,导致消息积压在Broker中。这会使Broker上有大量消息驻留内存,一旦内存耗尽就会导致消息不断从文件读取到内存,然后又被交换到文件中,从而消耗磁盘IO。更严重的是会拖累生产者,导致生产者一侧阻塞,从而减缓整个MQ的工作效率。目前,对这个问题,ActiveMQ使用等待消息限制策略(Pending Message Limit Strategy)来解决。
等待消息限制策略有两种:constantPendingMessageLimitStrategy和prefetchRatePendingMessageLimitStrategy。
constantPendingMessageLimitStrategy:0:不额外增加预取数量大小;大于0:额外增加预取数量大小;-1:不增加预取数量大小也不丢弃旧的消息。
<constantPendingMessageLimitStrategy limit="50"/>
prefetchRatePendingMessageLimitStrategy:利用消费者设置的预取数量乘以其倍数等于实际的预取数量大小。
<prefetchRatePendingMessageLimitStrategy multiplier="2.5"/>
消息丢弃策略有三种:oldestMessageEvictionStrategy(丢弃最旧的消息)、oldestMessageWithLowestPriorityEvictionStrategy(丢弃最旧的且优先级最低的消息)、uniquePropertyMessageEvictionStrategy(根据自定义的属性来丢弃消息)。
消息协议
ActiveMQ支持多种消息协议,也就是activemq.xml配置文件中transportConnectors结点下的配置。可以根据需要添加相应的协议,另外可以将参数挂在uri后面。
消息持久化
ActiveMQ支持多种持久化方式,当生产者发出消息后,Broker首先将消息存储到文件、内存数据库或远程数据库等,再将消息发送给消费者,发送成功后,将消息从存储中删除,若失败继续尝试发送。